mirror of
https://github.com/GAM-team/GAM.git
synced 2026-06-18 13:11:37 +00:00
GData reduction, security recs for regex
This commit is contained in:
6
.github/workflows/build.yml
vendored
6
.github/workflows/build.yml
vendored
@@ -665,7 +665,8 @@ jobs:
|
||||
$gam config csv_output_row_filter "email:regex:^gha_test_${JID}_" print cigroups | $gam csv - gam delete cigroup ~email
|
||||
$gam config csv_output_row_filter "resourceId:regex:^gha_test_${JID}_" print resources | $gam csv - gam delete resource ~resourceId
|
||||
$gam config csv_output_row_filter "buildingId:regex:^gha_test_${JID}_" print buildings | $gam csv - gam delete building ~buildingId
|
||||
|
||||
$gam config csv_output_row_filter "Emails.1.address:^gha_test-${JID}_" print contacts | $gam csv - gam delete contact ~ContactID
|
||||
|
||||
echo "Creating OrgUnit ${newou}"
|
||||
$gam create ou "${newou}"
|
||||
export GAM_THREADS=5
|
||||
@@ -684,6 +685,8 @@ jobs:
|
||||
$gam user $gam_user sendemail recipient $newuser subject "test message $newbase" message "GHA test message"
|
||||
$gam user $gam_user sendemail recipient exchange@pdl.jaylee.us subject "test ${tstamp}" message "test message"
|
||||
$gam config enable_dasa false save
|
||||
$gam create contact firstname GHA lastname "$JID" email "${newbase}@example.com"
|
||||
$gam print contacts
|
||||
$gam user $newuser add license workspaceenterpriseplus
|
||||
$gam print privileges
|
||||
$gam config enable_dasa true save
|
||||
@@ -781,6 +784,7 @@ jobs:
|
||||
$gam create device serialnumber $sn devicetype android
|
||||
$gam config enable_dasa true save
|
||||
$gam print users query "gha.jid=$JID" | $gam csv - gam delete user ~primaryEmail || if [ $? != 50 ]; then exit $?; fi # expect a 50 return code (vault hold on user)
|
||||
$gam delete contacts emailmatchpattern "^${newbase}@example.com$"
|
||||
$gam print mobile
|
||||
$gam print devices
|
||||
$gam print browsers
|
||||
|
||||
@@ -13864,7 +13864,8 @@ PC_PATTERN = re.compile(r'(?s){PC}.*?{/PC}')
|
||||
UC_PATTERN = re.compile(r'(?s){UC}.*?{/UC}')
|
||||
LC_PATTERN = re.compile(r'(?s){LC}.*?{/LC}')
|
||||
CASE_MARKERS = {'PC', '/PC', 'UC', '/UC', 'LC', '/LC'}
|
||||
SKIP_PATTERNS = [re.compile(r'<head>.*?</head>'), re.compile(r'<script>.*?</script>')]
|
||||
SKIP_PATTERNS = [re.compile(r'<head>.*?</head>', flags=re.IGNORECASE),
|
||||
re.compile(r'<script>.*?</script>', flags=re.IGNORECASE)]
|
||||
|
||||
def _processTagReplacements(tagReplacements, message):
|
||||
def pcase(trstring):
|
||||
|
||||
@@ -1,952 +0,0 @@
|
||||
#!/usr/bin/python
|
||||
#
|
||||
# Copyright (C) 2007 - 2009 Google Inc.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
|
||||
import cgi
|
||||
import math
|
||||
import random
|
||||
import re
|
||||
import time
|
||||
import types
|
||||
import urllib.request, urllib.parse, urllib.error
|
||||
import atom.http_interface
|
||||
import atom.token_store
|
||||
import atom.url
|
||||
import gdata.oauth as oauth
|
||||
import gdata.oauth.rsa as oauth_rsa
|
||||
import gdata.tlslite.utils.keyfactory as keyfactory
|
||||
from base64 import encodebytes
|
||||
|
||||
import gdata.gauth
|
||||
|
||||
__author__ = 'api.jscudder (Jeff Scudder)'
|
||||
|
||||
|
||||
PROGRAMMATIC_AUTH_LABEL = 'GoogleLogin auth='
|
||||
AUTHSUB_AUTH_LABEL = 'AuthSub token='
|
||||
|
||||
|
||||
"""This module provides functions and objects used with Google authentication.
|
||||
|
||||
Details on Google authorization mechanisms used with the Google Data APIs can
|
||||
be found here:
|
||||
http://code.google.com/apis/gdata/auth.html
|
||||
http://code.google.com/apis/accounts/
|
||||
|
||||
The essential functions are the following.
|
||||
Related to ClientLogin:
|
||||
generate_client_login_request_body: Constructs the body of an HTTP request to
|
||||
obtain a ClientLogin token for a specific
|
||||
service.
|
||||
extract_client_login_token: Creates a ClientLoginToken with the token from a
|
||||
success response to a ClientLogin request.
|
||||
get_captcha_challenge: If the server responded to the ClientLogin request
|
||||
with a CAPTCHA challenge, this method extracts the
|
||||
CAPTCHA URL and identifying CAPTCHA token.
|
||||
|
||||
Related to AuthSub:
|
||||
generate_auth_sub_url: Constructs a full URL for a AuthSub request. The
|
||||
user's browser must be sent to this Google Accounts
|
||||
URL and redirected back to the app to obtain the
|
||||
AuthSub token.
|
||||
extract_auth_sub_token_from_url: Once the user's browser has been
|
||||
redirected back to the web app, use this
|
||||
function to create an AuthSubToken with
|
||||
the correct authorization token and scope.
|
||||
token_from_http_body: Extracts the AuthSubToken value string from the
|
||||
server's response to an AuthSub session token upgrade
|
||||
request.
|
||||
"""
|
||||
|
||||
def generate_client_login_request_body(email, password, service, source,
|
||||
account_type='HOSTED_OR_GOOGLE', captcha_token=None,
|
||||
captcha_response=None):
|
||||
"""Creates the body of the autentication request
|
||||
|
||||
See http://code.google.com/apis/accounts/AuthForInstalledApps.html#Request
|
||||
for more details.
|
||||
|
||||
Args:
|
||||
email: str
|
||||
password: str
|
||||
service: str
|
||||
source: str
|
||||
account_type: str (optional) Defaul is 'HOSTED_OR_GOOGLE', other valid
|
||||
values are 'GOOGLE' and 'HOSTED'
|
||||
captcha_token: str (optional)
|
||||
captcha_response: str (optional)
|
||||
|
||||
Returns:
|
||||
The HTTP body to send in a request for a client login token.
|
||||
"""
|
||||
return gdata.gauth.generate_client_login_request_body(email, password,
|
||||
service, source, account_type, captcha_token, captcha_response)
|
||||
|
||||
|
||||
GenerateClientLoginRequestBody = generate_client_login_request_body
|
||||
|
||||
|
||||
def GenerateClientLoginAuthToken(http_body):
|
||||
"""Returns the token value to use in Authorization headers.
|
||||
|
||||
Reads the token from the server's response to a Client Login request and
|
||||
creates header value to use in requests.
|
||||
|
||||
Args:
|
||||
http_body: str The body of the server's HTTP response to a Client Login
|
||||
request
|
||||
|
||||
Returns:
|
||||
The value half of an Authorization header.
|
||||
"""
|
||||
token = get_client_login_token(http_body)
|
||||
if token:
|
||||
return 'GoogleLogin auth=%s' % token
|
||||
return None
|
||||
|
||||
|
||||
def get_client_login_token(http_body):
|
||||
"""Returns the token value for a ClientLoginToken.
|
||||
|
||||
Reads the token from the server's response to a Client Login request and
|
||||
creates the token value string to use in requests.
|
||||
|
||||
Args:
|
||||
http_body: str The body of the server's HTTP response to a Client Login
|
||||
request
|
||||
|
||||
Returns:
|
||||
The token value string for a ClientLoginToken.
|
||||
"""
|
||||
return gdata.gauth.get_client_login_token_string(http_body)
|
||||
|
||||
|
||||
def extract_client_login_token(http_body, scopes):
|
||||
"""Parses the server's response and returns a ClientLoginToken.
|
||||
|
||||
Args:
|
||||
http_body: str The body of the server's HTTP response to a Client Login
|
||||
request. It is assumed that the login request was successful.
|
||||
scopes: list containing atom.url.Urls or strs. The scopes list contains
|
||||
all of the partial URLs under which the client login token is
|
||||
valid. For example, if scopes contains ['http://example.com/foo']
|
||||
then the client login token would be valid for
|
||||
http://example.com/foo/bar/baz
|
||||
|
||||
Returns:
|
||||
A ClientLoginToken which is valid for the specified scopes.
|
||||
"""
|
||||
token_string = get_client_login_token(http_body)
|
||||
token = ClientLoginToken(scopes=scopes)
|
||||
token.set_token_string(token_string)
|
||||
return token
|
||||
|
||||
|
||||
def get_captcha_challenge(http_body,
|
||||
captcha_base_url='http://www.google.com/accounts/'):
|
||||
"""Returns the URL and token for a CAPTCHA challenge issued by the server.
|
||||
|
||||
Args:
|
||||
http_body: str The body of the HTTP response from the server which
|
||||
contains the CAPTCHA challenge.
|
||||
captcha_base_url: str This function returns a full URL for viewing the
|
||||
challenge image which is built from the server's response. This
|
||||
base_url is used as the beginning of the URL because the server
|
||||
only provides the end of the URL. For example the server provides
|
||||
'Captcha?ctoken=Hi...N' and the URL for the image is
|
||||
'http://www.google.com/accounts/Captcha?ctoken=Hi...N'
|
||||
|
||||
Returns:
|
||||
A dictionary containing the information needed to repond to the CAPTCHA
|
||||
challenge, the image URL and the ID token of the challenge. The
|
||||
dictionary is in the form:
|
||||
{'token': string identifying the CAPTCHA image,
|
||||
'url': string containing the URL of the image}
|
||||
Returns None if there was no CAPTCHA challenge in the response.
|
||||
"""
|
||||
return gdata.gauth.get_captcha_challenge(http_body, captcha_base_url)
|
||||
|
||||
|
||||
GetCaptchaChallenge = get_captcha_challenge
|
||||
|
||||
|
||||
def GenerateOAuthRequestTokenUrl(
|
||||
oauth_input_params, scopes,
|
||||
request_token_url='https://www.google.com/accounts/OAuthGetRequestToken',
|
||||
extra_parameters=None):
|
||||
"""Generate a URL at which a request for OAuth request token is to be sent.
|
||||
|
||||
Args:
|
||||
oauth_input_params: OAuthInputParams OAuth input parameters.
|
||||
scopes: list of strings The URLs of the services to be accessed.
|
||||
request_token_url: string The beginning of the request token URL. This is
|
||||
normally 'https://www.google.com/accounts/OAuthGetRequestToken' or
|
||||
'/accounts/OAuthGetRequestToken'
|
||||
extra_parameters: dict (optional) key-value pairs as any additional
|
||||
parameters to be included in the URL and signature while making a
|
||||
request for fetching an OAuth request token. All the OAuth parameters
|
||||
are added by default. But if provided through this argument, any
|
||||
default parameters will be overwritten. For e.g. a default parameter
|
||||
oauth_version 1.0 can be overwritten if
|
||||
extra_parameters = {'oauth_version': '2.0'}
|
||||
|
||||
Returns:
|
||||
atom.url.Url OAuth request token URL.
|
||||
"""
|
||||
scopes_string = ' '.join([str(scope) for scope in scopes])
|
||||
parameters = {'scope': scopes_string}
|
||||
if extra_parameters:
|
||||
parameters.update(extra_parameters)
|
||||
oauth_request = oauth.OAuthRequest.from_consumer_and_token(
|
||||
oauth_input_params.GetConsumer(), http_url=request_token_url,
|
||||
parameters=parameters)
|
||||
oauth_request.sign_request(oauth_input_params.GetSignatureMethod(),
|
||||
oauth_input_params.GetConsumer(), None)
|
||||
return atom.url.parse_url(oauth_request.to_url())
|
||||
|
||||
|
||||
def GenerateOAuthAuthorizationUrl(
|
||||
request_token,
|
||||
authorization_url='https://www.google.com/accounts/OAuthAuthorizeToken',
|
||||
callback_url=None, extra_params=None,
|
||||
include_scopes_in_callback=False, scopes_param_prefix='oauth_token_scope'):
|
||||
"""Generates URL at which user will login to authorize the request token.
|
||||
|
||||
Args:
|
||||
request_token: gdata.auth.OAuthToken OAuth request token.
|
||||
authorization_url: string The beginning of the authorization URL. This is
|
||||
normally 'https://www.google.com/accounts/OAuthAuthorizeToken' or
|
||||
'/accounts/OAuthAuthorizeToken'
|
||||
callback_url: string (optional) The URL user will be sent to after
|
||||
logging in and granting access.
|
||||
extra_params: dict (optional) Additional parameters to be sent.
|
||||
include_scopes_in_callback: Boolean (default=False) if set to True, and
|
||||
if 'callback_url' is present, the 'callback_url' will be modified to
|
||||
include the scope(s) from the request token as a URL parameter. The
|
||||
key for the 'callback' URL's scope parameter will be
|
||||
OAUTH_SCOPE_URL_PARAM_NAME. The benefit of including the scope URL as
|
||||
a parameter to the 'callback' URL, is that the page which receives
|
||||
the OAuth token will be able to tell which URLs the token grants
|
||||
access to.
|
||||
scopes_param_prefix: string (default='oauth_token_scope') The URL
|
||||
parameter key which maps to the list of valid scopes for the token.
|
||||
This URL parameter will be included in the callback URL along with
|
||||
the scopes of the token as value if include_scopes_in_callback=True.
|
||||
|
||||
Returns:
|
||||
atom.url.Url OAuth authorization URL.
|
||||
"""
|
||||
scopes = request_token.scopes
|
||||
if isinstance(scopes, list):
|
||||
scopes = ' '.join(scopes)
|
||||
if include_scopes_in_callback and callback_url:
|
||||
if callback_url.find('?') > -1:
|
||||
callback_url += '&'
|
||||
else:
|
||||
callback_url += '?'
|
||||
callback_url += urllib.parse.urlencode({scopes_param_prefix:scopes})
|
||||
oauth_token = oauth.OAuthToken(request_token.key, request_token.secret)
|
||||
oauth_request = oauth.OAuthRequest.from_token_and_callback(
|
||||
token=oauth_token, callback=callback_url,
|
||||
http_url=authorization_url, parameters=extra_params)
|
||||
return atom.url.parse_url(oauth_request.to_url())
|
||||
|
||||
|
||||
def GenerateOAuthAccessTokenUrl(
|
||||
authorized_request_token,
|
||||
oauth_input_params,
|
||||
access_token_url='https://www.google.com/accounts/OAuthGetAccessToken',
|
||||
oauth_version='1.0',
|
||||
oauth_verifier=None):
|
||||
"""Generates URL at which user will login to authorize the request token.
|
||||
|
||||
Args:
|
||||
authorized_request_token: gdata.auth.OAuthToken OAuth authorized request
|
||||
token.
|
||||
oauth_input_params: OAuthInputParams OAuth input parameters.
|
||||
access_token_url: string The beginning of the authorization URL. This is
|
||||
normally 'https://www.google.com/accounts/OAuthGetAccessToken' or
|
||||
'/accounts/OAuthGetAccessToken'
|
||||
oauth_version: str (default='1.0') oauth_version parameter.
|
||||
oauth_verifier: str (optional) If present, it is assumed that the client
|
||||
will use the OAuth v1.0a protocol which includes passing the
|
||||
oauth_verifier (as returned by the SP) in the access token step.
|
||||
|
||||
Returns:
|
||||
atom.url.Url OAuth access token URL.
|
||||
"""
|
||||
oauth_token = oauth.OAuthToken(authorized_request_token.key,
|
||||
authorized_request_token.secret)
|
||||
parameters = {'oauth_version': oauth_version}
|
||||
if oauth_verifier is not None:
|
||||
parameters['oauth_verifier'] = oauth_verifier
|
||||
oauth_request = oauth.OAuthRequest.from_consumer_and_token(
|
||||
oauth_input_params.GetConsumer(), token=oauth_token,
|
||||
http_url=access_token_url, parameters=parameters)
|
||||
oauth_request.sign_request(oauth_input_params.GetSignatureMethod(),
|
||||
oauth_input_params.GetConsumer(), oauth_token)
|
||||
return atom.url.parse_url(oauth_request.to_url())
|
||||
|
||||
|
||||
def GenerateAuthSubUrl(next, scope, secure=False, session=True,
|
||||
request_url='https://www.google.com/accounts/AuthSubRequest',
|
||||
domain='default'):
|
||||
"""Generate a URL at which the user will login and be redirected back.
|
||||
|
||||
Users enter their credentials on a Google login page and a token is sent
|
||||
to the URL specified in next. See documentation for AuthSub login at:
|
||||
http://code.google.com/apis/accounts/AuthForWebApps.html
|
||||
|
||||
Args:
|
||||
request_url: str The beginning of the request URL. This is normally
|
||||
'http://www.google.com/accounts/AuthSubRequest' or
|
||||
'/accounts/AuthSubRequest'
|
||||
next: string The URL user will be sent to after logging in.
|
||||
scope: string The URL of the service to be accessed.
|
||||
secure: boolean (optional) Determines whether or not the issued token
|
||||
is a secure token.
|
||||
session: boolean (optional) Determines whether or not the issued token
|
||||
can be upgraded to a session token.
|
||||
domain: str (optional) The Google Apps domain for this account. If this
|
||||
is not a Google Apps account, use 'default' which is the default
|
||||
value.
|
||||
"""
|
||||
# Translate True/False values for parameters into numeric values acceoted
|
||||
# by the AuthSub service.
|
||||
if secure:
|
||||
secure = 1
|
||||
else:
|
||||
secure = 0
|
||||
|
||||
if session:
|
||||
session = 1
|
||||
else:
|
||||
session = 0
|
||||
|
||||
request_params = urllib.parse.urlencode({'next': next, 'scope': scope,
|
||||
'secure': secure, 'session': session,
|
||||
'hd': domain})
|
||||
if request_url.find('?') == -1:
|
||||
return '%s?%s' % (request_url, request_params)
|
||||
else:
|
||||
# The request URL already contained url parameters so we should add
|
||||
# the parameters using the & seperator
|
||||
return '%s&%s' % (request_url, request_params)
|
||||
|
||||
|
||||
def generate_auth_sub_url(next, scopes, secure=False, session=True,
|
||||
request_url='https://www.google.com/accounts/AuthSubRequest',
|
||||
domain='default', scopes_param_prefix='auth_sub_scopes'):
|
||||
"""Constructs a URL string for requesting a multiscope AuthSub token.
|
||||
|
||||
The generated token will contain a URL parameter to pass along the
|
||||
requested scopes to the next URL. When the Google Accounts page
|
||||
redirects the broswser to the 'next' URL, it appends the single use
|
||||
AuthSub token value to the URL as a URL parameter with the key 'token'.
|
||||
However, the information about which scopes were requested is not
|
||||
included by Google Accounts. This method adds the scopes to the next
|
||||
URL before making the request so that the redirect will be sent to
|
||||
a page, and both the token value and the list of scopes can be
|
||||
extracted from the request URL.
|
||||
|
||||
Args:
|
||||
next: atom.url.URL or string The URL user will be sent to after
|
||||
authorizing this web application to access their data.
|
||||
scopes: list containint strings The URLs of the services to be accessed.
|
||||
secure: boolean (optional) Determines whether or not the issued token
|
||||
is a secure token.
|
||||
session: boolean (optional) Determines whether or not the issued token
|
||||
can be upgraded to a session token.
|
||||
request_url: atom.url.Url or str The beginning of the request URL. This
|
||||
is normally 'http://www.google.com/accounts/AuthSubRequest' or
|
||||
'/accounts/AuthSubRequest'
|
||||
domain: The domain which the account is part of. This is used for Google
|
||||
Apps accounts, the default value is 'default' which means that the
|
||||
requested account is a Google Account (@gmail.com for example)
|
||||
scopes_param_prefix: str (optional) The requested scopes are added as a
|
||||
URL parameter to the next URL so that the page at the 'next' URL can
|
||||
extract the token value and the valid scopes from the URL. The key
|
||||
for the URL parameter defaults to 'auth_sub_scopes'
|
||||
|
||||
Returns:
|
||||
An atom.url.Url which the user's browser should be directed to in order
|
||||
to authorize this application to access their information.
|
||||
"""
|
||||
if isinstance(next, str):
|
||||
next = atom.url.parse_url(next)
|
||||
scopes_string = ' '.join([str(scope) for scope in scopes])
|
||||
next.params[scopes_param_prefix] = scopes_string
|
||||
|
||||
if isinstance(request_url, str):
|
||||
request_url = atom.url.parse_url(request_url)
|
||||
request_url.params['next'] = str(next)
|
||||
request_url.params['scope'] = scopes_string
|
||||
if session:
|
||||
request_url.params['session'] = 1
|
||||
else:
|
||||
request_url.params['session'] = 0
|
||||
if secure:
|
||||
request_url.params['secure'] = 1
|
||||
else:
|
||||
request_url.params['secure'] = 0
|
||||
request_url.params['hd'] = domain
|
||||
return request_url
|
||||
|
||||
|
||||
def AuthSubTokenFromUrl(url):
|
||||
"""Extracts the AuthSub token from the URL.
|
||||
|
||||
Used after the AuthSub redirect has sent the user to the 'next' page and
|
||||
appended the token to the URL. This function returns the value to be used
|
||||
in the Authorization header.
|
||||
|
||||
Args:
|
||||
url: str The URL of the current page which contains the AuthSub token as
|
||||
a URL parameter.
|
||||
"""
|
||||
token = TokenFromUrl(url)
|
||||
if token:
|
||||
return 'AuthSub token=%s' % token
|
||||
return None
|
||||
|
||||
|
||||
def TokenFromUrl(url):
|
||||
"""Extracts the AuthSub token from the URL.
|
||||
|
||||
Returns the raw token value.
|
||||
|
||||
Args:
|
||||
url: str The URL or the query portion of the URL string (after the ?) of
|
||||
the current page which contains the AuthSub token as a URL parameter.
|
||||
"""
|
||||
if url.find('?') > -1:
|
||||
query_params = url.split('?')[1]
|
||||
else:
|
||||
query_params = url
|
||||
for pair in query_params.split('&'):
|
||||
if pair.startswith('token='):
|
||||
return pair[6:]
|
||||
return None
|
||||
|
||||
|
||||
def extract_auth_sub_token_from_url(url,
|
||||
scopes_param_prefix='auth_sub_scopes', rsa_key=None):
|
||||
"""Creates an AuthSubToken and sets the token value and scopes from the URL.
|
||||
|
||||
After the Google Accounts AuthSub pages redirect the user's broswer back to
|
||||
the web application (using the 'next' URL from the request) the web app must
|
||||
extract the token from the current page's URL. The token is provided as a
|
||||
URL parameter named 'token' and if generate_auth_sub_url was used to create
|
||||
the request, the token's valid scopes are included in a URL parameter whose
|
||||
name is specified in scopes_param_prefix.
|
||||
|
||||
Args:
|
||||
url: atom.url.Url or str representing the current URL. The token value
|
||||
and valid scopes should be included as URL parameters.
|
||||
scopes_param_prefix: str (optional) The URL parameter key which maps to
|
||||
the list of valid scopes for the token.
|
||||
|
||||
Returns:
|
||||
An AuthSubToken with the token value from the URL and set to be valid for
|
||||
the scopes passed in on the URL. If no scopes were included in the URL,
|
||||
the AuthSubToken defaults to being valid for no scopes. If there was no
|
||||
'token' parameter in the URL, this function returns None.
|
||||
"""
|
||||
if isinstance(url, str):
|
||||
url = atom.url.parse_url(url)
|
||||
if 'token' not in url.params:
|
||||
return None
|
||||
scopes = []
|
||||
if scopes_param_prefix in url.params:
|
||||
scopes = url.params[scopes_param_prefix].split(' ')
|
||||
token_value = url.params['token']
|
||||
if rsa_key:
|
||||
token = SecureAuthSubToken(rsa_key, scopes=scopes)
|
||||
else:
|
||||
token = AuthSubToken(scopes=scopes)
|
||||
token.set_token_string(token_value)
|
||||
return token
|
||||
|
||||
|
||||
def AuthSubTokenFromHttpBody(http_body):
|
||||
"""Extracts the AuthSub token from an HTTP body string.
|
||||
|
||||
Used to find the new session token after making a request to upgrade a
|
||||
single use AuthSub token.
|
||||
|
||||
Args:
|
||||
http_body: str The repsonse from the server which contains the AuthSub
|
||||
key. For example, this function would find the new session token
|
||||
from the server's response to an upgrade token request.
|
||||
|
||||
Returns:
|
||||
The header value to use for Authorization which contains the AuthSub
|
||||
token.
|
||||
"""
|
||||
token_value = token_from_http_body(http_body)
|
||||
if token_value:
|
||||
return '%s%s' % (AUTHSUB_AUTH_LABEL, token_value)
|
||||
return None
|
||||
|
||||
|
||||
def token_from_http_body(http_body):
|
||||
"""Extracts the AuthSub token from an HTTP body string.
|
||||
|
||||
Used to find the new session token after making a request to upgrade a
|
||||
single use AuthSub token.
|
||||
|
||||
Args:
|
||||
http_body: str The repsonse from the server which contains the AuthSub
|
||||
key. For example, this function would find the new session token
|
||||
from the server's response to an upgrade token request.
|
||||
|
||||
Returns:
|
||||
The raw token value to use in an AuthSubToken object.
|
||||
"""
|
||||
for response_line in http_body.splitlines():
|
||||
if response_line.startswith('Token='):
|
||||
# Strip off Token= and return the token value string.
|
||||
return response_line[6:]
|
||||
return None
|
||||
|
||||
|
||||
TokenFromHttpBody = token_from_http_body
|
||||
|
||||
|
||||
def OAuthTokenFromUrl(url, scopes_param_prefix='oauth_token_scope'):
|
||||
"""Creates an OAuthToken and sets token key and scopes (if present) from URL.
|
||||
|
||||
After the Google Accounts OAuth pages redirect the user's broswer back to
|
||||
the web application (using the 'callback' URL from the request) the web app
|
||||
can extract the token from the current page's URL. The token is same as the
|
||||
request token, but it is either authorized (if user grants access) or
|
||||
unauthorized (if user denies access). The token is provided as a
|
||||
URL parameter named 'oauth_token' and if it was chosen to use
|
||||
GenerateOAuthAuthorizationUrl with include_scopes_in_param=True, the token's
|
||||
valid scopes are included in a URL parameter whose name is specified in
|
||||
scopes_param_prefix.
|
||||
|
||||
Args:
|
||||
url: atom.url.Url or str representing the current URL. The token value
|
||||
and valid scopes should be included as URL parameters.
|
||||
scopes_param_prefix: str (optional) The URL parameter key which maps to
|
||||
the list of valid scopes for the token.
|
||||
|
||||
Returns:
|
||||
An OAuthToken with the token key from the URL and set to be valid for
|
||||
the scopes passed in on the URL. If no scopes were included in the URL,
|
||||
the OAuthToken defaults to being valid for no scopes. If there was no
|
||||
'oauth_token' parameter in the URL, this function returns None.
|
||||
"""
|
||||
if isinstance(url, str):
|
||||
url = atom.url.parse_url(url)
|
||||
if 'oauth_token' not in url.params:
|
||||
return None
|
||||
scopes = []
|
||||
if scopes_param_prefix in url.params:
|
||||
scopes = url.params[scopes_param_prefix].split(' ')
|
||||
token_key = url.params['oauth_token']
|
||||
token = OAuthToken(key=token_key, scopes=scopes)
|
||||
return token
|
||||
|
||||
|
||||
def OAuthTokenFromHttpBody(http_body):
|
||||
"""Parses the HTTP response body and returns an OAuth token.
|
||||
|
||||
The returned OAuth token will just have key and secret parameters set.
|
||||
It won't have any knowledge about the scopes or oauth_input_params. It is
|
||||
your responsibility to make it aware of the remaining parameters.
|
||||
|
||||
Returns:
|
||||
OAuthToken OAuth token.
|
||||
"""
|
||||
token = oauth.OAuthToken.from_string(http_body)
|
||||
oauth_token = OAuthToken(key=token.key, secret=token.secret)
|
||||
return oauth_token
|
||||
|
||||
|
||||
class OAuthSignatureMethod(object):
|
||||
"""Holds valid OAuth signature methods.
|
||||
|
||||
RSA_SHA1: Class to build signature according to RSA-SHA1 algorithm.
|
||||
HMAC_SHA1: Class to build signature according to HMAC-SHA1 algorithm.
|
||||
"""
|
||||
|
||||
HMAC_SHA1 = oauth.OAuthSignatureMethod_HMAC_SHA1
|
||||
|
||||
class RSA_SHA1(oauth_rsa.OAuthSignatureMethod_RSA_SHA1):
|
||||
"""Provides implementation for abstract methods to return RSA certs."""
|
||||
|
||||
def __init__(self, private_key, public_cert):
|
||||
self.private_key = private_key
|
||||
self.public_cert = public_cert
|
||||
|
||||
def _fetch_public_cert(self, unused_oauth_request):
|
||||
return self.public_cert
|
||||
|
||||
def _fetch_private_cert(self, unused_oauth_request):
|
||||
return self.private_key
|
||||
|
||||
|
||||
class OAuthInputParams(object):
|
||||
"""Stores OAuth input parameters.
|
||||
|
||||
This class is a store for OAuth input parameters viz. consumer key and secret,
|
||||
signature method and RSA key.
|
||||
"""
|
||||
|
||||
def __init__(self, signature_method, consumer_key, consumer_secret=None,
|
||||
rsa_key=None, requestor_id=None):
|
||||
"""Initializes object with parameters required for using OAuth mechanism.
|
||||
|
||||
NOTE: Though consumer_secret and rsa_key are optional, either of the two
|
||||
is required depending on the value of the signature_method.
|
||||
|
||||
Args:
|
||||
signature_method: class which provides implementation for strategy class
|
||||
oauth.oauth.OAuthSignatureMethod. Signature method to be used for
|
||||
signing each request. Valid implementations are provided as the
|
||||
constants defined by gdata.auth.OAuthSignatureMethod. Currently
|
||||
they are gdata.auth.OAuthSignatureMethod.RSA_SHA1 and
|
||||
gdata.auth.OAuthSignatureMethod.HMAC_SHA1. Instead of passing in
|
||||
the strategy class, you may pass in a string for 'RSA_SHA1' or
|
||||
'HMAC_SHA1'. If you plan to use OAuth on App Engine (or another
|
||||
WSGI environment) I recommend specifying signature method using a
|
||||
string (the only options are 'RSA_SHA1' and 'HMAC_SHA1'). In these
|
||||
environments there are sometimes issues with pickling an object in
|
||||
which a member references a class or function. Storing a string to
|
||||
refer to the signature method mitigates complications when
|
||||
pickling.
|
||||
consumer_key: string Domain identifying third_party web application.
|
||||
consumer_secret: string (optional) Secret generated during registration.
|
||||
Required only for HMAC_SHA1 signature method.
|
||||
rsa_key: string (optional) Private key required for RSA_SHA1 signature
|
||||
method.
|
||||
requestor_id: string (optional) User email adress to make requests on
|
||||
their behalf. This parameter should only be set when performing
|
||||
2 legged OAuth requests.
|
||||
"""
|
||||
if (signature_method == OAuthSignatureMethod.RSA_SHA1
|
||||
or signature_method == 'RSA_SHA1'):
|
||||
self.__signature_strategy = 'RSA_SHA1'
|
||||
elif (signature_method == OAuthSignatureMethod.HMAC_SHA1
|
||||
or signature_method == 'HMAC_SHA1'):
|
||||
self.__signature_strategy = 'HMAC_SHA1'
|
||||
else:
|
||||
self.__signature_strategy = signature_method
|
||||
self.rsa_key = rsa_key
|
||||
self._consumer = oauth.OAuthConsumer(consumer_key, consumer_secret)
|
||||
self.requestor_id = requestor_id
|
||||
|
||||
def __get_signature_method(self):
|
||||
if self.__signature_strategy == 'RSA_SHA1':
|
||||
return OAuthSignatureMethod.RSA_SHA1(self.rsa_key, None)
|
||||
elif self.__signature_strategy == 'HMAC_SHA1':
|
||||
return OAuthSignatureMethod.HMAC_SHA1()
|
||||
else:
|
||||
return self.__signature_strategy()
|
||||
|
||||
def __set_signature_method(self, signature_method):
|
||||
if (signature_method == OAuthSignatureMethod.RSA_SHA1
|
||||
or signature_method == 'RSA_SHA1'):
|
||||
self.__signature_strategy = 'RSA_SHA1'
|
||||
elif (signature_method == OAuthSignatureMethod.HMAC_SHA1
|
||||
or signature_method == 'HMAC_SHA1'):
|
||||
self.__signature_strategy = 'HMAC_SHA1'
|
||||
else:
|
||||
self.__signature_strategy = signature_method
|
||||
|
||||
_signature_method = property(__get_signature_method, __set_signature_method,
|
||||
doc="""Returns object capable of signing the request using RSA of HMAC.
|
||||
|
||||
Replaces the _signature_method member to avoid pickle errors.""")
|
||||
|
||||
def GetSignatureMethod(self):
|
||||
"""Gets the OAuth signature method.
|
||||
|
||||
Returns:
|
||||
object of supertype <oauth.oauth.OAuthSignatureMethod>
|
||||
"""
|
||||
return self._signature_method
|
||||
|
||||
def GetConsumer(self):
|
||||
"""Gets the OAuth consumer.
|
||||
|
||||
Returns:
|
||||
object of type <oauth.oauth.Consumer>
|
||||
"""
|
||||
return self._consumer
|
||||
|
||||
|
||||
class ClientLoginToken(atom.http_interface.GenericToken):
|
||||
"""Stores the Authorization header in auth_header and adds to requests.
|
||||
|
||||
This token will add it's Authorization header to an HTTP request
|
||||
as it is made. Ths token class is simple but
|
||||
some Token classes must calculate portions of the Authorization header
|
||||
based on the request being made, which is why the token is responsible
|
||||
for making requests via an http_client parameter.
|
||||
|
||||
Args:
|
||||
auth_header: str The value for the Authorization header.
|
||||
scopes: list of str or atom.url.Url specifying the beginnings of URLs
|
||||
for which this token can be used. For example, if scopes contains
|
||||
'http://example.com/foo', then this token can be used for a request to
|
||||
'http://example.com/foo/bar' but it cannot be used for a request to
|
||||
'http://example.com/baz'
|
||||
"""
|
||||
def __init__(self, auth_header=None, scopes=None):
|
||||
self.auth_header = auth_header
|
||||
self.scopes = scopes or []
|
||||
|
||||
def __str__(self):
|
||||
return self.auth_header
|
||||
|
||||
def perform_request(self, http_client, operation, url, data=None,
|
||||
headers=None):
|
||||
"""Sets the Authorization header and makes the HTTP request."""
|
||||
if headers is None:
|
||||
headers = {'Authorization':self.auth_header}
|
||||
else:
|
||||
headers['Authorization'] = self.auth_header
|
||||
return http_client.request(operation, url, data=data, headers=headers)
|
||||
|
||||
def get_token_string(self):
|
||||
"""Removes PROGRAMMATIC_AUTH_LABEL to give just the token value."""
|
||||
return self.auth_header[len(PROGRAMMATIC_AUTH_LABEL):]
|
||||
|
||||
def set_token_string(self, token_string):
|
||||
self.auth_header = '%s%s' % (PROGRAMMATIC_AUTH_LABEL, token_string)
|
||||
|
||||
def valid_for_scope(self, url):
|
||||
"""Tells the caller if the token authorizes access to the desired URL.
|
||||
"""
|
||||
if isinstance(url, str):
|
||||
url = atom.url.parse_url(url)
|
||||
for scope in self.scopes:
|
||||
if scope == atom.token_store.SCOPE_ALL:
|
||||
return True
|
||||
if isinstance(scope, str):
|
||||
scope = atom.url.parse_url(scope)
|
||||
if scope == url:
|
||||
return True
|
||||
# Check the host and the path, but ignore the port and protocol.
|
||||
elif scope.host == url.host and not scope.path:
|
||||
return True
|
||||
elif scope.host == url.host and scope.path and not url.path:
|
||||
continue
|
||||
elif scope.host == url.host and url.path.startswith(scope.path):
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
class AuthSubToken(ClientLoginToken):
|
||||
def get_token_string(self):
|
||||
"""Removes AUTHSUB_AUTH_LABEL to give just the token value."""
|
||||
return self.auth_header[len(AUTHSUB_AUTH_LABEL):]
|
||||
|
||||
def set_token_string(self, token_string):
|
||||
self.auth_header = '%s%s' % (AUTHSUB_AUTH_LABEL, token_string)
|
||||
|
||||
|
||||
class OAuthToken(atom.http_interface.GenericToken):
|
||||
"""Stores the token key, token secret and scopes for which token is valid.
|
||||
|
||||
This token adds the authorization header to each request made. It
|
||||
re-calculates authorization header for every request since the OAuth
|
||||
signature to be added to the authorization header is dependent on the
|
||||
request parameters.
|
||||
|
||||
Attributes:
|
||||
key: str The value for the OAuth token i.e. token key.
|
||||
secret: str The value for the OAuth token secret.
|
||||
scopes: list of str or atom.url.Url specifying the beginnings of URLs
|
||||
for which this token can be used. For example, if scopes contains
|
||||
'http://example.com/foo', then this token can be used for a request to
|
||||
'http://example.com/foo/bar' but it cannot be used for a request to
|
||||
'http://example.com/baz'
|
||||
oauth_input_params: OAuthInputParams OAuth input parameters.
|
||||
"""
|
||||
|
||||
def __init__(self, key=None, secret=None, scopes=None,
|
||||
oauth_input_params=None):
|
||||
self.key = key
|
||||
self.secret = secret
|
||||
self.scopes = scopes or []
|
||||
self.oauth_input_params = oauth_input_params
|
||||
|
||||
def __str__(self):
|
||||
return self.get_token_string()
|
||||
|
||||
def get_token_string(self):
|
||||
"""Returns the token string.
|
||||
|
||||
The token string returned is of format
|
||||
oauth_token=[0]&oauth_token_secret=[1], where [0] and [1] are some strings.
|
||||
|
||||
Returns:
|
||||
A token string of format oauth_token=[0]&oauth_token_secret=[1],
|
||||
where [0] and [1] are some strings. If self.secret is absent, it just
|
||||
returns oauth_token=[0]. If self.key is absent, it just returns
|
||||
oauth_token_secret=[1]. If both are absent, it returns None.
|
||||
"""
|
||||
if self.key and self.secret:
|
||||
return urllib.parse.urlencode({'oauth_token': self.key,
|
||||
'oauth_token_secret': self.secret})
|
||||
elif self.key:
|
||||
return 'oauth_token=%s' % self.key
|
||||
elif self.secret:
|
||||
return 'oauth_token_secret=%s' % self.secret
|
||||
else:
|
||||
return None
|
||||
|
||||
def set_token_string(self, token_string):
|
||||
"""Sets the token key and secret from the token string.
|
||||
|
||||
Args:
|
||||
token_string: str Token string of form
|
||||
oauth_token=[0]&oauth_token_secret=[1]. If oauth_token is not present,
|
||||
self.key will be None. If oauth_token_secret is not present,
|
||||
self.secret will be None.
|
||||
"""
|
||||
token_params = cgi.parse_qs(token_string, keep_blank_values=False)
|
||||
if 'oauth_token' in token_params:
|
||||
self.key = token_params['oauth_token'][0]
|
||||
if 'oauth_token_secret' in token_params:
|
||||
self.secret = token_params['oauth_token_secret'][0]
|
||||
|
||||
def GetAuthHeader(self, http_method, http_url, realm=''):
|
||||
"""Get the authentication header.
|
||||
|
||||
Args:
|
||||
http_method: string HTTP method i.e. operation e.g. GET, POST, PUT, etc.
|
||||
http_url: string or atom.url.Url HTTP URL to which request is made.
|
||||
realm: string (default='') realm parameter to be included in the
|
||||
authorization header.
|
||||
|
||||
Returns:
|
||||
dict Header to be sent with every subsequent request after
|
||||
authentication.
|
||||
"""
|
||||
if isinstance(http_url, str):
|
||||
http_url = atom.url.parse_url(http_url)
|
||||
header = None
|
||||
token = None
|
||||
if self.key or self.secret:
|
||||
token = oauth.OAuthToken(self.key, self.secret)
|
||||
oauth_request = oauth.OAuthRequest.from_consumer_and_token(
|
||||
self.oauth_input_params.GetConsumer(), token=token,
|
||||
http_url=str(http_url), http_method=http_method,
|
||||
parameters=http_url.params)
|
||||
oauth_request.sign_request(self.oauth_input_params.GetSignatureMethod(),
|
||||
self.oauth_input_params.GetConsumer(), token)
|
||||
header = oauth_request.to_header(realm=realm)
|
||||
header['Authorization'] = header['Authorization'].replace('+', '%2B')
|
||||
return header
|
||||
|
||||
def perform_request(self, http_client, operation, url, data=None,
|
||||
headers=None):
|
||||
"""Sets the Authorization header and makes the HTTP request."""
|
||||
if not headers:
|
||||
headers = {}
|
||||
if self.oauth_input_params.requestor_id:
|
||||
url.params['xoauth_requestor_id'] = self.oauth_input_params.requestor_id
|
||||
headers.update(self.GetAuthHeader(operation, url))
|
||||
return http_client.request(operation, url, data=data, headers=headers)
|
||||
|
||||
def valid_for_scope(self, url):
|
||||
if isinstance(url, str):
|
||||
url = atom.url.parse_url(url)
|
||||
for scope in self.scopes:
|
||||
if scope == atom.token_store.SCOPE_ALL:
|
||||
return True
|
||||
if isinstance(scope, str):
|
||||
scope = atom.url.parse_url(scope)
|
||||
if scope == url:
|
||||
return True
|
||||
# Check the host and the path, but ignore the port and protocol.
|
||||
elif scope.host == url.host and not scope.path:
|
||||
return True
|
||||
elif scope.host == url.host and scope.path and not url.path:
|
||||
continue
|
||||
elif scope.host == url.host and url.path.startswith(scope.path):
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
class SecureAuthSubToken(AuthSubToken):
|
||||
"""Stores the rsa private key, token, and scopes for the secure AuthSub token.
|
||||
|
||||
This token adds the authorization header to each request made. It
|
||||
re-calculates authorization header for every request since the secure AuthSub
|
||||
signature to be added to the authorization header is dependent on the
|
||||
request parameters.
|
||||
|
||||
Attributes:
|
||||
rsa_key: string The RSA private key in PEM format that the token will
|
||||
use to sign requests
|
||||
token_string: string (optional) The value for the AuthSub token.
|
||||
scopes: list of str or atom.url.Url specifying the beginnings of URLs
|
||||
for which this token can be used. For example, if scopes contains
|
||||
'http://example.com/foo', then this token can be used for a request to
|
||||
'http://example.com/foo/bar' but it cannot be used for a request to
|
||||
'http://example.com/baz'
|
||||
"""
|
||||
|
||||
def __init__(self, rsa_key, token_string=None, scopes=None):
|
||||
self.rsa_key = keyfactory.parsePEMKey(rsa_key)
|
||||
self.token_string = token_string or ''
|
||||
self.scopes = scopes or []
|
||||
|
||||
def __str__(self):
|
||||
return self.get_token_string()
|
||||
|
||||
def get_token_string(self):
|
||||
return str(self.token_string)
|
||||
|
||||
def set_token_string(self, token_string):
|
||||
self.token_string = token_string
|
||||
|
||||
def GetAuthHeader(self, http_method, http_url):
|
||||
"""Generates the Authorization header.
|
||||
|
||||
The form of the secure AuthSub Authorization header is
|
||||
Authorization: AuthSub token="token" sigalg="sigalg" data="data" sig="sig"
|
||||
and data represents a string in the form
|
||||
data = http_method http_url timestamp nonce
|
||||
|
||||
Args:
|
||||
http_method: string HTTP method i.e. operation e.g. GET, POST, PUT, etc.
|
||||
http_url: string or atom.url.Url HTTP URL to which request is made.
|
||||
|
||||
Returns:
|
||||
dict Header to be sent with every subsequent request after authentication.
|
||||
"""
|
||||
timestamp = int(math.floor(time.time()))
|
||||
nonce = '%lu' % random.randrange(1, 2**64)
|
||||
data = '%s %s %d %s' % (http_method, str(http_url), timestamp, nonce)
|
||||
sig = encodebytes(str(self.rsa_key.hashAndSign(data))).rstrip()
|
||||
header = {'Authorization': '%s"%s" data="%s" sig="%s" sigalg="rsa-sha1"' %
|
||||
(AUTHSUB_AUTH_LABEL, self.token_string, data, sig)}
|
||||
return header
|
||||
|
||||
def perform_request(self, http_client, operation, url, data=None,
|
||||
headers=None):
|
||||
"""Sets the Authorization header and makes the HTTP request."""
|
||||
if not headers:
|
||||
headers = {}
|
||||
headers.update(self.GetAuthHeader(operation, url))
|
||||
return http_client.request(operation, url, data=data, headers=headers)
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,17 +0,0 @@
|
||||
1. Moved oauth.py to __init__.py
|
||||
|
||||
2. Refactored __init__.py for compatibility with python 2.2 (Issue 59)
|
||||
|
||||
3. Refactored rsa.py for compatibility with python 2.2 (Issue 59)
|
||||
|
||||
4. Refactored OAuthRequest.from_token_and_callback since the callback url was
|
||||
getting double url-encoding the callback url in place of single. (Issue 43)
|
||||
|
||||
5. Added build_signature_base_string method to rsa.py since it used the
|
||||
implementation of this method from oauth.OAuthSignatureMethod_HMAC_SHA1 which
|
||||
was incorrect since it enforced the presence of a consumer secret and a token
|
||||
secret. Also, changed its super class from oauth.OAuthSignatureMethod_HMAC_SHA1
|
||||
to oauth.OAuthSignatureMethod (Issue 64)
|
||||
|
||||
6. Refactored <OAuthRequest>.to_header method since it returned non-oauth params
|
||||
as well which was incorrect. (Issue 31)
|
||||
@@ -1,524 +0,0 @@
|
||||
import cgi
|
||||
import urllib.request, urllib.parse, urllib.error
|
||||
import time
|
||||
import random
|
||||
import urllib.parse
|
||||
import hmac
|
||||
import binascii
|
||||
|
||||
VERSION = '1.0' # Hi Blaine!
|
||||
HTTP_METHOD = 'GET'
|
||||
SIGNATURE_METHOD = 'PLAINTEXT'
|
||||
|
||||
# Generic exception class
|
||||
class OAuthError(RuntimeError):
|
||||
def __init__(self, message='OAuth error occured.'):
|
||||
self.message = message
|
||||
|
||||
# optional WWW-Authenticate header (401 error)
|
||||
def build_authenticate_header(realm=''):
|
||||
return {'WWW-Authenticate': 'OAuth realm="%s"' % realm}
|
||||
|
||||
# url escape
|
||||
def escape(s):
|
||||
# escape '/' too
|
||||
return urllib.parse.quote(s, safe='~')
|
||||
|
||||
# util function: current timestamp
|
||||
# seconds since epoch (UTC)
|
||||
def generate_timestamp():
|
||||
return int(time.time())
|
||||
|
||||
# util function: nonce
|
||||
# pseudorandom number
|
||||
def generate_nonce(length=8):
|
||||
return ''.join([str(random.randint(0, 9)) for i in range(length)])
|
||||
|
||||
# OAuthConsumer is a data type that represents the identity of the Consumer
|
||||
# via its shared secret with the Service Provider.
|
||||
class OAuthConsumer(object):
|
||||
key = None
|
||||
secret = None
|
||||
|
||||
def __init__(self, key, secret):
|
||||
self.key = key
|
||||
self.secret = secret
|
||||
|
||||
# OAuthToken is a data type that represents an End User via either an access
|
||||
# or request token.
|
||||
class OAuthToken(object):
|
||||
# access tokens and request tokens
|
||||
key = None
|
||||
secret = None
|
||||
|
||||
'''
|
||||
key = the token
|
||||
secret = the token secret
|
||||
'''
|
||||
def __init__(self, key, secret):
|
||||
self.key = key
|
||||
self.secret = secret
|
||||
|
||||
def to_string(self):
|
||||
return urllib.parse.urlencode({'oauth_token': self.key, 'oauth_token_secret': self.secret})
|
||||
|
||||
# return a token from something like:
|
||||
# oauth_token_secret=digg&oauth_token=digg
|
||||
def from_string(s):
|
||||
params = cgi.parse_qs(s, keep_blank_values=False)
|
||||
key = params['oauth_token'][0]
|
||||
secret = params['oauth_token_secret'][0]
|
||||
return OAuthToken(key, secret)
|
||||
from_string = staticmethod(from_string)
|
||||
|
||||
def __str__(self):
|
||||
return self.to_string()
|
||||
|
||||
# OAuthRequest represents the request and can be serialized
|
||||
class OAuthRequest(object):
|
||||
'''
|
||||
OAuth parameters:
|
||||
- oauth_consumer_key
|
||||
- oauth_token
|
||||
- oauth_signature_method
|
||||
- oauth_signature
|
||||
- oauth_timestamp
|
||||
- oauth_nonce
|
||||
- oauth_version
|
||||
... any additional parameters, as defined by the Service Provider.
|
||||
'''
|
||||
parameters = None # oauth parameters
|
||||
http_method = HTTP_METHOD
|
||||
http_url = None
|
||||
version = VERSION
|
||||
|
||||
def __init__(self, http_method=HTTP_METHOD, http_url=None, parameters=None):
|
||||
self.http_method = http_method
|
||||
self.http_url = http_url
|
||||
self.parameters = parameters or {}
|
||||
|
||||
def set_parameter(self, parameter, value):
|
||||
self.parameters[parameter] = value
|
||||
|
||||
def get_parameter(self, parameter):
|
||||
try:
|
||||
return self.parameters[parameter]
|
||||
except:
|
||||
raise OAuthError('Parameter not found: %s' % parameter)
|
||||
|
||||
def _get_timestamp_nonce(self):
|
||||
return self.get_parameter('oauth_timestamp'), self.get_parameter('oauth_nonce')
|
||||
|
||||
# get any non-oauth parameters
|
||||
def get_nonoauth_parameters(self):
|
||||
parameters = {}
|
||||
for k, v in self.parameters.items():
|
||||
# ignore oauth parameters
|
||||
if k.find('oauth_') < 0:
|
||||
parameters[k] = v
|
||||
return parameters
|
||||
|
||||
# serialize as a header for an HTTPAuth request
|
||||
def to_header(self, realm=''):
|
||||
auth_header = 'OAuth realm="%s"' % realm
|
||||
# add the oauth parameters
|
||||
if self.parameters:
|
||||
for k, v in self.parameters.items():
|
||||
if k[:6] == 'oauth_':
|
||||
auth_header += ', %s="%s"' % (k, escape(str(v)))
|
||||
return {'Authorization': auth_header}
|
||||
|
||||
# serialize as post data for a POST request
|
||||
def to_postdata(self):
|
||||
return '&'.join(['%s=%s' % (escape(str(k)), escape(str(v))) for k, v in self.parameters.items()])
|
||||
|
||||
# serialize as a url for a GET request
|
||||
def to_url(self):
|
||||
return '%s?%s' % (self.get_normalized_http_url(), self.to_postdata())
|
||||
|
||||
# return a string that consists of all the parameters that need to be signed
|
||||
def get_normalized_parameters(self):
|
||||
params = self.parameters
|
||||
try:
|
||||
# exclude the signature if it exists
|
||||
del params['oauth_signature']
|
||||
except:
|
||||
pass
|
||||
key_values = list(params.items())
|
||||
# sort lexicographically, first after key, then after value
|
||||
key_values.sort()
|
||||
# combine key value pairs in string and escape
|
||||
return '&'.join(['%s=%s' % (escape(str(k)), escape(str(v))) for k, v in key_values])
|
||||
|
||||
# just uppercases the http method
|
||||
def get_normalized_http_method(self):
|
||||
return self.http_method.upper()
|
||||
|
||||
# parses the url and rebuilds it to be scheme://host/path
|
||||
def get_normalized_http_url(self):
|
||||
parts = urllib.parse.urlparse(self.http_url)
|
||||
url_string = '%s://%s%s' % (parts[0], parts[1], parts[2]) # scheme, netloc, path
|
||||
return url_string
|
||||
|
||||
# set the signature parameter to the result of build_signature
|
||||
def sign_request(self, signature_method, consumer, token):
|
||||
# set the signature method
|
||||
self.set_parameter('oauth_signature_method', signature_method.get_name())
|
||||
# set the signature
|
||||
self.set_parameter('oauth_signature', self.build_signature(signature_method, consumer, token))
|
||||
|
||||
def build_signature(self, signature_method, consumer, token):
|
||||
# call the build signature method within the signature method
|
||||
return signature_method.build_signature(self, consumer, token)
|
||||
|
||||
def from_request(http_method, http_url, headers=None, parameters=None, query_string=None):
|
||||
# combine multiple parameter sources
|
||||
if parameters is None:
|
||||
parameters = {}
|
||||
|
||||
# headers
|
||||
if headers and 'Authorization' in headers:
|
||||
auth_header = headers['Authorization']
|
||||
# check that the authorization header is OAuth
|
||||
if auth_header.index('OAuth') > -1:
|
||||
try:
|
||||
# get the parameters from the header
|
||||
header_params = OAuthRequest._split_header(auth_header)
|
||||
parameters.update(header_params)
|
||||
except:
|
||||
raise OAuthError('Unable to parse OAuth parameters from Authorization header.')
|
||||
|
||||
# GET or POST query string
|
||||
if query_string:
|
||||
query_params = OAuthRequest._split_url_string(query_string)
|
||||
parameters.update(query_params)
|
||||
|
||||
# URL parameters
|
||||
param_str = urllib.parse.urlparse(http_url)[4] # query
|
||||
url_params = OAuthRequest._split_url_string(param_str)
|
||||
parameters.update(url_params)
|
||||
|
||||
if parameters:
|
||||
return OAuthRequest(http_method, http_url, parameters)
|
||||
|
||||
return None
|
||||
from_request = staticmethod(from_request)
|
||||
|
||||
def from_consumer_and_token(oauth_consumer, token=None, http_method=HTTP_METHOD, http_url=None, parameters=None):
|
||||
if not parameters:
|
||||
parameters = {}
|
||||
|
||||
defaults = {
|
||||
'oauth_consumer_key': oauth_consumer.key,
|
||||
'oauth_timestamp': generate_timestamp(),
|
||||
'oauth_nonce': generate_nonce(),
|
||||
'oauth_version': OAuthRequest.version,
|
||||
}
|
||||
|
||||
defaults.update(parameters)
|
||||
parameters = defaults
|
||||
|
||||
if token:
|
||||
parameters['oauth_token'] = token.key
|
||||
|
||||
return OAuthRequest(http_method, http_url, parameters)
|
||||
from_consumer_and_token = staticmethod(from_consumer_and_token)
|
||||
|
||||
def from_token_and_callback(token, callback=None, http_method=HTTP_METHOD, http_url=None, parameters=None):
|
||||
if not parameters:
|
||||
parameters = {}
|
||||
|
||||
parameters['oauth_token'] = token.key
|
||||
|
||||
if callback:
|
||||
parameters['oauth_callback'] = callback
|
||||
|
||||
return OAuthRequest(http_method, http_url, parameters)
|
||||
from_token_and_callback = staticmethod(from_token_and_callback)
|
||||
|
||||
# util function: turn Authorization: header into parameters, has to do some unescaping
|
||||
def _split_header(header):
|
||||
params = {}
|
||||
parts = header.split(',')
|
||||
for param in parts:
|
||||
# ignore realm parameter
|
||||
if param.find('OAuth realm') > -1:
|
||||
continue
|
||||
# remove whitespace
|
||||
param = param.strip()
|
||||
# split key-value
|
||||
param_parts = param.split('=', 1)
|
||||
# remove quotes and unescape the value
|
||||
params[param_parts[0]] = urllib.parse.unquote(param_parts[1].strip('\"'))
|
||||
return params
|
||||
_split_header = staticmethod(_split_header)
|
||||
|
||||
# util function: turn url string into parameters, has to do some unescaping
|
||||
def _split_url_string(param_str):
|
||||
parameters = cgi.parse_qs(param_str, keep_blank_values=False)
|
||||
for k, v in parameters.items():
|
||||
parameters[k] = urllib.parse.unquote(v[0])
|
||||
return parameters
|
||||
_split_url_string = staticmethod(_split_url_string)
|
||||
|
||||
# OAuthServer is a worker to check a requests validity against a data store
|
||||
class OAuthServer(object):
|
||||
timestamp_threshold = 300 # in seconds, five minutes
|
||||
version = VERSION
|
||||
signature_methods = None
|
||||
data_store = None
|
||||
|
||||
def __init__(self, data_store=None, signature_methods=None):
|
||||
self.data_store = data_store
|
||||
self.signature_methods = signature_methods or {}
|
||||
|
||||
def set_data_store(self, oauth_data_store):
|
||||
self.data_store = data_store
|
||||
|
||||
def get_data_store(self):
|
||||
return self.data_store
|
||||
|
||||
def add_signature_method(self, signature_method):
|
||||
self.signature_methods[signature_method.get_name()] = signature_method
|
||||
return self.signature_methods
|
||||
|
||||
# process a request_token request
|
||||
# returns the request token on success
|
||||
def fetch_request_token(self, oauth_request):
|
||||
try:
|
||||
# get the request token for authorization
|
||||
token = self._get_token(oauth_request, 'request')
|
||||
except OAuthError:
|
||||
# no token required for the initial token request
|
||||
version = self._get_version(oauth_request)
|
||||
consumer = self._get_consumer(oauth_request)
|
||||
self._check_signature(oauth_request, consumer, None)
|
||||
# fetch a new token
|
||||
token = self.data_store.fetch_request_token(consumer)
|
||||
return token
|
||||
|
||||
# process an access_token request
|
||||
# returns the access token on success
|
||||
def fetch_access_token(self, oauth_request):
|
||||
version = self._get_version(oauth_request)
|
||||
consumer = self._get_consumer(oauth_request)
|
||||
# get the request token
|
||||
token = self._get_token(oauth_request, 'request')
|
||||
self._check_signature(oauth_request, consumer, token)
|
||||
new_token = self.data_store.fetch_access_token(consumer, token)
|
||||
return new_token
|
||||
|
||||
# verify an api call, checks all the parameters
|
||||
def verify_request(self, oauth_request):
|
||||
# -> consumer and token
|
||||
version = self._get_version(oauth_request)
|
||||
consumer = self._get_consumer(oauth_request)
|
||||
# get the access token
|
||||
token = self._get_token(oauth_request, 'access')
|
||||
self._check_signature(oauth_request, consumer, token)
|
||||
parameters = oauth_request.get_nonoauth_parameters()
|
||||
return consumer, token, parameters
|
||||
|
||||
# authorize a request token
|
||||
def authorize_token(self, token, user):
|
||||
return self.data_store.authorize_request_token(token, user)
|
||||
|
||||
# get the callback url
|
||||
def get_callback(self, oauth_request):
|
||||
return oauth_request.get_parameter('oauth_callback')
|
||||
|
||||
# optional support for the authenticate header
|
||||
def build_authenticate_header(self, realm=''):
|
||||
return {'WWW-Authenticate': 'OAuth realm="%s"' % realm}
|
||||
|
||||
# verify the correct version request for this server
|
||||
def _get_version(self, oauth_request):
|
||||
try:
|
||||
version = oauth_request.get_parameter('oauth_version')
|
||||
except:
|
||||
version = VERSION
|
||||
if version and version != self.version:
|
||||
raise OAuthError('OAuth version %s not supported.' % str(version))
|
||||
return version
|
||||
|
||||
# figure out the signature with some defaults
|
||||
def _get_signature_method(self, oauth_request):
|
||||
try:
|
||||
signature_method = oauth_request.get_parameter('oauth_signature_method')
|
||||
except:
|
||||
signature_method = SIGNATURE_METHOD
|
||||
try:
|
||||
# get the signature method object
|
||||
signature_method = self.signature_methods[signature_method]
|
||||
except:
|
||||
signature_method_names = ', '.join(list(self.signature_methods.keys()))
|
||||
raise OAuthError('Signature method %s not supported try one of the following: %s' % (signature_method, signature_method_names))
|
||||
|
||||
return signature_method
|
||||
|
||||
def _get_consumer(self, oauth_request):
|
||||
consumer_key = oauth_request.get_parameter('oauth_consumer_key')
|
||||
if not consumer_key:
|
||||
raise OAuthError('Invalid consumer key.')
|
||||
consumer = self.data_store.lookup_consumer(consumer_key)
|
||||
if not consumer:
|
||||
raise OAuthError('Invalid consumer.')
|
||||
return consumer
|
||||
|
||||
# try to find the token for the provided request token key
|
||||
def _get_token(self, oauth_request, token_type='access'):
|
||||
token_field = oauth_request.get_parameter('oauth_token')
|
||||
token = self.data_store.lookup_token(token_type, token_field)
|
||||
if not token:
|
||||
raise OAuthError('Invalid %s token: %s' % (token_type, token_field))
|
||||
return token
|
||||
|
||||
def _check_signature(self, oauth_request, consumer, token):
|
||||
timestamp, nonce = oauth_request._get_timestamp_nonce()
|
||||
self._check_timestamp(timestamp)
|
||||
self._check_nonce(consumer, token, nonce)
|
||||
signature_method = self._get_signature_method(oauth_request)
|
||||
try:
|
||||
signature = oauth_request.get_parameter('oauth_signature')
|
||||
except:
|
||||
raise OAuthError('Missing signature.')
|
||||
# validate the signature
|
||||
valid_sig = signature_method.check_signature(oauth_request, consumer, token, signature)
|
||||
if not valid_sig:
|
||||
key, base = signature_method.build_signature_base_string(oauth_request, consumer, token)
|
||||
raise OAuthError('Invalid signature. Expected signature base string: %s' % base)
|
||||
built = signature_method.build_signature(oauth_request, consumer, token)
|
||||
|
||||
def _check_timestamp(self, timestamp):
|
||||
# verify that timestamp is recentish
|
||||
timestamp = int(timestamp)
|
||||
now = int(time.time())
|
||||
lapsed = now - timestamp
|
||||
if lapsed > self.timestamp_threshold:
|
||||
raise OAuthError('Expired timestamp: given %d and now %s has a greater difference than threshold %d' % (timestamp, now, self.timestamp_threshold))
|
||||
|
||||
def _check_nonce(self, consumer, token, nonce):
|
||||
# verify that the nonce is uniqueish
|
||||
nonce = self.data_store.lookup_nonce(consumer, token, nonce)
|
||||
if nonce:
|
||||
raise OAuthError('Nonce already used: %s' % str(nonce))
|
||||
|
||||
# OAuthClient is a worker to attempt to execute a request
|
||||
class OAuthClient(object):
|
||||
consumer = None
|
||||
token = None
|
||||
|
||||
def __init__(self, oauth_consumer, oauth_token):
|
||||
self.consumer = oauth_consumer
|
||||
self.token = oauth_token
|
||||
|
||||
def get_consumer(self):
|
||||
return self.consumer
|
||||
|
||||
def get_token(self):
|
||||
return self.token
|
||||
|
||||
def fetch_request_token(self, oauth_request):
|
||||
# -> OAuthToken
|
||||
raise NotImplementedError
|
||||
|
||||
def fetch_access_token(self, oauth_request):
|
||||
# -> OAuthToken
|
||||
raise NotImplementedError
|
||||
|
||||
def access_resource(self, oauth_request):
|
||||
# -> some protected resource
|
||||
raise NotImplementedError
|
||||
|
||||
# OAuthDataStore is a database abstraction used to lookup consumers and tokens
|
||||
class OAuthDataStore(object):
|
||||
|
||||
def lookup_consumer(self, key):
|
||||
# -> OAuthConsumer
|
||||
raise NotImplementedError
|
||||
|
||||
def lookup_token(self, oauth_consumer, token_type, token_token):
|
||||
# -> OAuthToken
|
||||
raise NotImplementedError
|
||||
|
||||
def lookup_nonce(self, oauth_consumer, oauth_token, nonce, timestamp):
|
||||
# -> OAuthToken
|
||||
raise NotImplementedError
|
||||
|
||||
def fetch_request_token(self, oauth_consumer):
|
||||
# -> OAuthToken
|
||||
raise NotImplementedError
|
||||
|
||||
def fetch_access_token(self, oauth_consumer, oauth_token):
|
||||
# -> OAuthToken
|
||||
raise NotImplementedError
|
||||
|
||||
def authorize_request_token(self, oauth_token, user):
|
||||
# -> OAuthToken
|
||||
raise NotImplementedError
|
||||
|
||||
# OAuthSignatureMethod is a strategy class that implements a signature method
|
||||
class OAuthSignatureMethod(object):
|
||||
def get_name(self):
|
||||
# -> str
|
||||
raise NotImplementedError
|
||||
|
||||
def build_signature_base_string(self, oauth_request, oauth_consumer, oauth_token):
|
||||
# -> str key, str raw
|
||||
raise NotImplementedError
|
||||
|
||||
def build_signature(self, oauth_request, oauth_consumer, oauth_token):
|
||||
# -> str
|
||||
raise NotImplementedError
|
||||
|
||||
def check_signature(self, oauth_request, consumer, token, signature):
|
||||
built = self.build_signature(oauth_request, consumer, token)
|
||||
return built == signature
|
||||
|
||||
class OAuthSignatureMethod_HMAC_SHA1(OAuthSignatureMethod):
|
||||
|
||||
def get_name(self):
|
||||
return 'HMAC-SHA1'
|
||||
|
||||
def build_signature_base_string(self, oauth_request, consumer, token):
|
||||
sig = (
|
||||
escape(oauth_request.get_normalized_http_method()),
|
||||
escape(oauth_request.get_normalized_http_url()),
|
||||
escape(oauth_request.get_normalized_parameters()),
|
||||
)
|
||||
|
||||
key = '%s&' % escape(consumer.secret)
|
||||
if token:
|
||||
key += escape(token.secret)
|
||||
raw = '&'.join(sig)
|
||||
return key, raw
|
||||
|
||||
def build_signature(self, oauth_request, consumer, token):
|
||||
# build the base signature string
|
||||
key, raw = self.build_signature_base_string(oauth_request, consumer, token)
|
||||
|
||||
# hmac object
|
||||
try:
|
||||
import hashlib # 2.5
|
||||
hashed = hmac.new(key, raw, hashlib.sha1)
|
||||
except:
|
||||
import sha # deprecated
|
||||
hashed = hmac.new(key, raw, sha)
|
||||
|
||||
# calculate the digest base 64
|
||||
return binascii.b2a_base64(hashed.digest())[:-1]
|
||||
|
||||
class OAuthSignatureMethod_PLAINTEXT(OAuthSignatureMethod):
|
||||
|
||||
def get_name(self):
|
||||
return 'PLAINTEXT'
|
||||
|
||||
def build_signature_base_string(self, oauth_request, consumer, token):
|
||||
# concatenate the consumer key and secret
|
||||
sig = escape(consumer.secret) + '&'
|
||||
if token:
|
||||
sig = sig + escape(token.secret)
|
||||
return sig
|
||||
|
||||
def build_signature(self, oauth_request, consumer, token):
|
||||
return self.build_signature_base_string(oauth_request, consumer, token)
|
||||
@@ -1,120 +0,0 @@
|
||||
#!/usr/bin/python
|
||||
|
||||
"""
|
||||
requires tlslite - http://trevp.net/tlslite/
|
||||
|
||||
"""
|
||||
|
||||
import binascii
|
||||
|
||||
from gdata.tlslite.utils import keyfactory
|
||||
from gdata.tlslite.utils import cryptomath
|
||||
|
||||
# XXX andy: ugly local import due to module name, oauth.oauth
|
||||
import gdata.oauth as oauth
|
||||
|
||||
class OAuthSignatureMethod_RSA_SHA1(oauth.OAuthSignatureMethod):
|
||||
def get_name(self):
|
||||
return "RSA-SHA1"
|
||||
|
||||
def _fetch_public_cert(self, oauth_request):
|
||||
# not implemented yet, ideas are:
|
||||
# (1) do a lookup in a table of trusted certs keyed off of consumer
|
||||
# (2) fetch via http using a url provided by the requester
|
||||
# (3) some sort of specific discovery code based on request
|
||||
#
|
||||
# either way should return a string representation of the certificate
|
||||
raise NotImplementedError
|
||||
|
||||
def _fetch_private_cert(self, oauth_request):
|
||||
# not implemented yet, ideas are:
|
||||
# (1) do a lookup in a table of trusted certs keyed off of consumer
|
||||
#
|
||||
# either way should return a string representation of the certificate
|
||||
raise NotImplementedError
|
||||
|
||||
def build_signature_base_string(self, oauth_request, consumer, token):
|
||||
sig = (
|
||||
oauth.escape(oauth_request.get_normalized_http_method()),
|
||||
oauth.escape(oauth_request.get_normalized_http_url()),
|
||||
oauth.escape(oauth_request.get_normalized_parameters()),
|
||||
)
|
||||
key = ''
|
||||
raw = '&'.join(sig)
|
||||
return key, raw
|
||||
|
||||
def build_signature(self, oauth_request, consumer, token):
|
||||
key, base_string = self.build_signature_base_string(oauth_request,
|
||||
consumer,
|
||||
token)
|
||||
|
||||
# Fetch the private key cert based on the request
|
||||
cert = self._fetch_private_cert(oauth_request)
|
||||
|
||||
# Pull the private key from the certificate
|
||||
privatekey = keyfactory.parsePrivateKey(cert)
|
||||
|
||||
# Convert base_string to bytes
|
||||
#base_string_bytes = cryptomath.createByteArraySequence(base_string)
|
||||
|
||||
# Sign using the key
|
||||
signed = privatekey.hashAndSign(base_string)
|
||||
|
||||
return binascii.b2a_base64(signed)[:-1]
|
||||
|
||||
def check_signature(self, oauth_request, consumer, token, signature):
|
||||
decoded_sig = base64.b64decode(signature);
|
||||
|
||||
key, base_string = self.build_signature_base_string(oauth_request,
|
||||
consumer,
|
||||
token)
|
||||
|
||||
# Fetch the public key cert based on the request
|
||||
cert = self._fetch_public_cert(oauth_request)
|
||||
|
||||
# Pull the public key from the certificate
|
||||
publickey = keyfactory.parsePEMKey(cert, public=True)
|
||||
|
||||
# Check the signature
|
||||
ok = publickey.hashAndVerify(decoded_sig, base_string)
|
||||
|
||||
return ok
|
||||
|
||||
|
||||
class TestOAuthSignatureMethod_RSA_SHA1(OAuthSignatureMethod_RSA_SHA1):
|
||||
def _fetch_public_cert(self, oauth_request):
|
||||
cert = """
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIBpjCCAQ+gAwIBAgIBATANBgkqhkiG9w0BAQUFADAZMRcwFQYDVQQDDA5UZXN0
|
||||
IFByaW5jaXBhbDAeFw03MDAxMDEwODAwMDBaFw0zODEyMzEwODAwMDBaMBkxFzAV
|
||||
BgNVBAMMDlRlc3QgUHJpbmNpcGFsMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKB
|
||||
gQC0YjCwIfYoprq/FQO6lb3asXrxLlJFuCvtinTF5p0GxvQGu5O3gYytUvtC2JlY
|
||||
zypSRjVxwxrsuRcP3e641SdASwfrmzyvIgP08N4S0IFzEURkV1wp/IpH7kH41Etb
|
||||
mUmrXSwfNZsnQRE5SYSOhh+LcK2wyQkdgcMv11l4KoBkcwIDAQABMA0GCSqGSIb3
|
||||
DQEBBQUAA4GBAGZLPEuJ5SiJ2ryq+CmEGOXfvlTtEL2nuGtr9PewxkgnOjZpUy+d
|
||||
4TvuXJbNQc8f4AMWL/tO9w0Fk80rWKp9ea8/df4qMq5qlFWlx6yOLQxumNOmECKb
|
||||
WpkUQDIDJEoFUzKMVuJf4KO/FJ345+BNLGgbJ6WujreoM1X/gYfdnJ/J
|
||||
-----END CERTIFICATE-----
|
||||
"""
|
||||
return cert
|
||||
|
||||
def _fetch_private_cert(self, oauth_request):
|
||||
cert = """
|
||||
-----BEGIN PRIVATE KEY-----
|
||||
MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBALRiMLAh9iimur8V
|
||||
A7qVvdqxevEuUkW4K+2KdMXmnQbG9Aa7k7eBjK1S+0LYmVjPKlJGNXHDGuy5Fw/d
|
||||
7rjVJ0BLB+ubPK8iA/Tw3hLQgXMRRGRXXCn8ikfuQfjUS1uZSatdLB81mydBETlJ
|
||||
hI6GH4twrbDJCR2Bwy/XWXgqgGRzAgMBAAECgYBYWVtleUzavkbrPjy0T5FMou8H
|
||||
X9u2AC2ry8vD/l7cqedtwMPp9k7TubgNFo+NGvKsl2ynyprOZR1xjQ7WgrgVB+mm
|
||||
uScOM/5HVceFuGRDhYTCObE+y1kxRloNYXnx3ei1zbeYLPCHdhxRYW7T0qcynNmw
|
||||
rn05/KO2RLjgQNalsQJBANeA3Q4Nugqy4QBUCEC09SqylT2K9FrrItqL2QKc9v0Z
|
||||
zO2uwllCbg0dwpVuYPYXYvikNHHg+aCWF+VXsb9rpPsCQQDWR9TT4ORdzoj+Nccn
|
||||
qkMsDmzt0EfNaAOwHOmVJ2RVBspPcxt5iN4HI7HNeG6U5YsFBb+/GZbgfBT3kpNG
|
||||
WPTpAkBI+gFhjfJvRw38n3g/+UeAkwMI2TJQS4n8+hid0uus3/zOjDySH3XHCUno
|
||||
cn1xOJAyZODBo47E+67R4jV1/gzbAkEAklJaspRPXP877NssM5nAZMU0/O/NGCZ+
|
||||
3jPgDUno6WbJn5cqm8MqWhW1xGkImgRk+fkDBquiq4gPiT898jusgQJAd5Zrr6Q8
|
||||
AO/0isr/3aa6O6NLQxISLKcPDk2NOccAfS/xOtfOz4sJYM3+Bs4Io9+dZGSDCA54
|
||||
Lw03eHTNQghS0A==
|
||||
-----END PRIVATE KEY-----
|
||||
"""
|
||||
return cert
|
||||
@@ -69,8 +69,6 @@ import gdata
|
||||
import atom
|
||||
import atom.http_interface
|
||||
import atom.token_store
|
||||
import gdata.auth
|
||||
import gdata.gauth
|
||||
|
||||
|
||||
AUTH_SERVER_HOST = 'https://www.google.com'
|
||||
@@ -87,8 +85,6 @@ SCOPE_URL_PARAM_NAME = 'authsub_token_scope'
|
||||
# 'callback' URL which contains the requested scope. This constant is the
|
||||
# default name (AKA key) for the URL parameter.
|
||||
OAUTH_SCOPE_URL_PARAM_NAME = 'oauth_token_scope'
|
||||
# Maps the service names used in ClientLogin to scope URLs.
|
||||
CLIENT_LOGIN_SCOPES = gdata.gauth.AUTH_SCOPES
|
||||
# Default parameters for GDataService.GetWithRetries method
|
||||
DEFAULT_NUM_RETRIES = 3
|
||||
DEFAULT_DELAY = 1
|
||||
@@ -1091,7 +1087,7 @@ class GDataService(atom.service.AtomService):
|
||||
location = (server_response.getheader('Location')
|
||||
or server_response.getheader('location'))
|
||||
if location is not None:
|
||||
m = re.compile('[\?\&]gsessionid=(\w*)').search(location)
|
||||
m = re.compile('[?&]gsessionid=(\w*)').search(location)
|
||||
if m is not None:
|
||||
self.__gsessionid = m.group(1)
|
||||
return GDataService.Get(self, location, extra_headers, redirects_remaining - 1,
|
||||
@@ -1339,7 +1335,7 @@ class GDataService(atom.service.AtomService):
|
||||
location = (server_response.getheader('Location')
|
||||
or server_response.getheader('location'))
|
||||
if location is not None:
|
||||
m = re.compile('[\?\&]gsessionid=(\w*)').search(location)
|
||||
m = re.compile('[?&]gsessionid=(\w*)').search(location)
|
||||
if m is not None:
|
||||
self.__gsessionid = m.group(1)
|
||||
return GDataService.PostOrPut(self, verb, data, location,
|
||||
@@ -1437,7 +1433,7 @@ class GDataService(atom.service.AtomService):
|
||||
location = (server_response.getheader('Location')
|
||||
or server_response.getheader('location'))
|
||||
if location is not None:
|
||||
m = re.compile('[\?\&]gsessionid=(\w*)').search(location)
|
||||
m = re.compile('[?&]gsessionid=(\w*)').search(location)
|
||||
if m is not None:
|
||||
self.__gsessionid = m.group(1)
|
||||
return GDataService.Delete(self, location, extra_headers,
|
||||
|
||||
@@ -1,120 +0,0 @@
|
||||
"""Base class for SharedKeyDB and VerifierDB."""
|
||||
|
||||
import dbm
|
||||
import _thread
|
||||
|
||||
class BaseDB:
|
||||
def __init__(self, filename, type):
|
||||
self.type = type
|
||||
self.filename = filename
|
||||
if self.filename:
|
||||
self.db = None
|
||||
else:
|
||||
self.db = {}
|
||||
self.lock = _thread.allocate_lock()
|
||||
|
||||
def create(self):
|
||||
"""Create a new on-disk database.
|
||||
|
||||
@raise anydbm.error: If there's a problem creating the database.
|
||||
"""
|
||||
if self.filename:
|
||||
self.db = dbm.open(self.filename, "n") #raises anydbm.error
|
||||
self.db["--Reserved--type"] = self.type
|
||||
self.db.sync()
|
||||
else:
|
||||
self.db = {}
|
||||
|
||||
def open(self):
|
||||
"""Open a pre-existing on-disk database.
|
||||
|
||||
@raise anydbm.error: If there's a problem opening the database.
|
||||
@raise ValueError: If the database is not of the right type.
|
||||
"""
|
||||
if not self.filename:
|
||||
raise ValueError("Can only open on-disk databases")
|
||||
self.db = dbm.open(self.filename, "w") #raises anydbm.error
|
||||
try:
|
||||
if self.db["--Reserved--type"] != self.type:
|
||||
raise ValueError("Not a %s database" % self.type)
|
||||
except KeyError:
|
||||
raise ValueError("Not a recognized database")
|
||||
|
||||
def __getitem__(self, username):
|
||||
if self.db == None:
|
||||
raise AssertionError("DB not open")
|
||||
|
||||
self.lock.acquire()
|
||||
try:
|
||||
valueStr = self.db[username]
|
||||
finally:
|
||||
self.lock.release()
|
||||
|
||||
return self._getItem(username, valueStr)
|
||||
|
||||
def __setitem__(self, username, value):
|
||||
if self.db == None:
|
||||
raise AssertionError("DB not open")
|
||||
|
||||
valueStr = self._setItem(username, value)
|
||||
|
||||
self.lock.acquire()
|
||||
try:
|
||||
self.db[username] = valueStr
|
||||
if self.filename:
|
||||
self.db.sync()
|
||||
finally:
|
||||
self.lock.release()
|
||||
|
||||
def __delitem__(self, username):
|
||||
if self.db == None:
|
||||
raise AssertionError("DB not open")
|
||||
|
||||
self.lock.acquire()
|
||||
try:
|
||||
del(self.db[username])
|
||||
if self.filename:
|
||||
self.db.sync()
|
||||
finally:
|
||||
self.lock.release()
|
||||
|
||||
def __contains__(self, username):
|
||||
"""Check if the database contains the specified username.
|
||||
|
||||
@type username: str
|
||||
@param username: The username to check for.
|
||||
|
||||
@rtype: bool
|
||||
@return: True if the database contains the username, False
|
||||
otherwise.
|
||||
|
||||
"""
|
||||
if self.db == None:
|
||||
raise AssertionError("DB not open")
|
||||
|
||||
self.lock.acquire()
|
||||
try:
|
||||
return username in self.db
|
||||
finally:
|
||||
self.lock.release()
|
||||
|
||||
def check(self, username, param):
|
||||
value = self.__getitem__(username)
|
||||
return self._checkItem(value, username, param)
|
||||
|
||||
def keys(self):
|
||||
"""Return a list of usernames in the database.
|
||||
|
||||
@rtype: list
|
||||
@return: The usernames in the database.
|
||||
"""
|
||||
if self.db == None:
|
||||
raise AssertionError("DB not open")
|
||||
|
||||
self.lock.acquire()
|
||||
try:
|
||||
usernames = list(self.db.keys())
|
||||
finally:
|
||||
self.lock.release()
|
||||
usernames = [u for u in usernames if not u.startswith("--Reserved--")]
|
||||
return usernames
|
||||
@@ -1,146 +0,0 @@
|
||||
"""Class for post-handshake certificate checking."""
|
||||
|
||||
from .utils.cryptomath import hashAndBase64
|
||||
from .X509 import X509
|
||||
from .X509CertChain import X509CertChain
|
||||
from .errors import *
|
||||
|
||||
|
||||
class Checker:
|
||||
"""This class is passed to a handshake function to check the other
|
||||
party's certificate chain.
|
||||
|
||||
If a handshake function completes successfully, but the Checker
|
||||
judges the other party's certificate chain to be missing or
|
||||
inadequate, a subclass of
|
||||
L{tlslite.errors.TLSAuthenticationError} will be raised.
|
||||
|
||||
Currently, the Checker can check either an X.509 or a cryptoID
|
||||
chain (for the latter, cryptoIDlib must be installed).
|
||||
"""
|
||||
|
||||
def __init__(self, cryptoID=None, protocol=None,
|
||||
x509Fingerprint=None,
|
||||
x509TrustList=None, x509CommonName=None,
|
||||
checkResumedSession=False):
|
||||
"""Create a new Checker instance.
|
||||
|
||||
You must pass in one of these argument combinations:
|
||||
- cryptoID[, protocol] (requires cryptoIDlib)
|
||||
- x509Fingerprint
|
||||
- x509TrustList[, x509CommonName] (requires cryptlib_py)
|
||||
|
||||
@type cryptoID: str
|
||||
@param cryptoID: A cryptoID which the other party's certificate
|
||||
chain must match. The cryptoIDlib module must be installed.
|
||||
Mutually exclusive with all of the 'x509...' arguments.
|
||||
|
||||
@type protocol: str
|
||||
@param protocol: A cryptoID protocol URI which the other
|
||||
party's certificate chain must match. Requires the 'cryptoID'
|
||||
argument.
|
||||
|
||||
@type x509Fingerprint: str
|
||||
@param x509Fingerprint: A hex-encoded X.509 end-entity
|
||||
fingerprint which the other party's end-entity certificate must
|
||||
match. Mutually exclusive with the 'cryptoID' and
|
||||
'x509TrustList' arguments.
|
||||
|
||||
@type x509TrustList: list of L{tlslite.X509.X509}
|
||||
@param x509TrustList: A list of trusted root certificates. The
|
||||
other party must present a certificate chain which extends to
|
||||
one of these root certificates. The cryptlib_py module must be
|
||||
installed. Mutually exclusive with the 'cryptoID' and
|
||||
'x509Fingerprint' arguments.
|
||||
|
||||
@type x509CommonName: str
|
||||
@param x509CommonName: The end-entity certificate's 'CN' field
|
||||
must match this value. For a web server, this is typically a
|
||||
server name such as 'www.amazon.com'. Mutually exclusive with
|
||||
the 'cryptoID' and 'x509Fingerprint' arguments. Requires the
|
||||
'x509TrustList' argument.
|
||||
|
||||
@type checkResumedSession: bool
|
||||
@param checkResumedSession: If resumed sessions should be
|
||||
checked. This defaults to False, on the theory that if the
|
||||
session was checked once, we don't need to bother
|
||||
re-checking it.
|
||||
"""
|
||||
|
||||
if cryptoID and (x509Fingerprint or x509TrustList):
|
||||
raise ValueError()
|
||||
if x509Fingerprint and x509TrustList:
|
||||
raise ValueError()
|
||||
if x509CommonName and not x509TrustList:
|
||||
raise ValueError()
|
||||
if protocol and not cryptoID:
|
||||
raise ValueError()
|
||||
if cryptoID:
|
||||
import cryptoIDlib #So we raise an error here
|
||||
if x509TrustList:
|
||||
import cryptlib_py #So we raise an error here
|
||||
self.cryptoID = cryptoID
|
||||
self.protocol = protocol
|
||||
self.x509Fingerprint = x509Fingerprint
|
||||
self.x509TrustList = x509TrustList
|
||||
self.x509CommonName = x509CommonName
|
||||
self.checkResumedSession = checkResumedSession
|
||||
|
||||
def __call__(self, connection):
|
||||
"""Check a TLSConnection.
|
||||
|
||||
When a Checker is passed to a handshake function, this will
|
||||
be called at the end of the function.
|
||||
|
||||
@type connection: L{tlslite.TLSConnection.TLSConnection}
|
||||
@param connection: The TLSConnection to examine.
|
||||
|
||||
@raise tlslite.errors.TLSAuthenticationError: If the other
|
||||
party's certificate chain is missing or bad.
|
||||
"""
|
||||
if not self.checkResumedSession and connection.resumed:
|
||||
return
|
||||
|
||||
if self.cryptoID or self.x509Fingerprint or self.x509TrustList:
|
||||
if connection._client:
|
||||
chain = connection.session.serverCertChain
|
||||
else:
|
||||
chain = connection.session.clientCertChain
|
||||
|
||||
if self.x509Fingerprint or self.x509TrustList:
|
||||
if isinstance(chain, X509CertChain):
|
||||
if self.x509Fingerprint:
|
||||
if chain.getFingerprint() != self.x509Fingerprint:
|
||||
raise TLSFingerprintError(\
|
||||
"X.509 fingerprint mismatch: %s, %s" % \
|
||||
(chain.getFingerprint(), self.x509Fingerprint))
|
||||
else: #self.x509TrustList
|
||||
if not chain.validate(self.x509TrustList):
|
||||
raise TLSValidationError("X.509 validation failure")
|
||||
if self.x509CommonName and \
|
||||
(chain.getCommonName() != self.x509CommonName):
|
||||
raise TLSAuthorizationError(\
|
||||
"X.509 Common Name mismatch: %s, %s" % \
|
||||
(chain.getCommonName(), self.x509CommonName))
|
||||
elif chain:
|
||||
raise TLSAuthenticationTypeError()
|
||||
else:
|
||||
raise TLSNoAuthenticationError()
|
||||
elif self.cryptoID:
|
||||
import cryptoIDlib.CertChain
|
||||
if isinstance(chain, cryptoIDlib.CertChain.CertChain):
|
||||
if chain.cryptoID != self.cryptoID:
|
||||
raise TLSFingerprintError(\
|
||||
"cryptoID mismatch: %s, %s" % \
|
||||
(chain.cryptoID, self.cryptoID))
|
||||
if self.protocol:
|
||||
if not chain.checkProtocol(self.protocol):
|
||||
raise TLSAuthorizationError(\
|
||||
"cryptoID protocol mismatch")
|
||||
if not chain.validate():
|
||||
raise TLSValidationError("cryptoID validation failure")
|
||||
elif chain:
|
||||
raise TLSAuthenticationTypeError()
|
||||
else:
|
||||
raise TLSNoAuthenticationError()
|
||||
|
||||
@@ -1,220 +0,0 @@
|
||||
"""Class returned by TLSConnection.makefile()."""
|
||||
|
||||
class FileObject:
|
||||
"""This class provides a file object interface to a
|
||||
L{tlslite.TLSConnection.TLSConnection}.
|
||||
|
||||
Call makefile() on a TLSConnection to create a FileObject instance.
|
||||
|
||||
This class was copied, with minor modifications, from the
|
||||
_fileobject class in socket.py. Note that fileno() is not
|
||||
implemented."""
|
||||
|
||||
default_bufsize = 16384 #TREV: changed from 8192
|
||||
|
||||
def __init__(self, sock, mode='rb', bufsize=-1):
|
||||
self._sock = sock
|
||||
self.mode = mode # Not actually used in this version
|
||||
if bufsize < 0:
|
||||
bufsize = self.default_bufsize
|
||||
self.bufsize = bufsize
|
||||
self.softspace = False
|
||||
if bufsize == 0:
|
||||
self._rbufsize = 1
|
||||
elif bufsize == 1:
|
||||
self._rbufsize = self.default_bufsize
|
||||
else:
|
||||
self._rbufsize = bufsize
|
||||
self._wbufsize = bufsize
|
||||
self._rbuf = "" # A string
|
||||
self._wbuf = [] # A list of strings
|
||||
|
||||
def _getclosed(self):
|
||||
return self._sock is not None
|
||||
closed = property(_getclosed, doc="True if the file is closed")
|
||||
|
||||
def close(self):
|
||||
try:
|
||||
if self._sock:
|
||||
for result in self._sock._decrefAsync(): #TREV
|
||||
pass
|
||||
finally:
|
||||
self._sock = None
|
||||
|
||||
def __del__(self):
|
||||
try:
|
||||
self.close()
|
||||
except:
|
||||
# close() may fail if __init__ didn't complete
|
||||
pass
|
||||
|
||||
def flush(self):
|
||||
if self._wbuf:
|
||||
buffer = "".join(self._wbuf)
|
||||
self._wbuf = []
|
||||
self._sock.sendall(buffer)
|
||||
|
||||
#def fileno(self):
|
||||
# raise NotImplementedError() #TREV
|
||||
|
||||
def write(self, data):
|
||||
data = str(data) # XXX Should really reject non-string non-buffers
|
||||
if not data:
|
||||
return
|
||||
self._wbuf.append(data)
|
||||
if (self._wbufsize == 0 or
|
||||
self._wbufsize == 1 and '\n' in data or
|
||||
self._get_wbuf_len() >= self._wbufsize):
|
||||
self.flush()
|
||||
|
||||
def writelines(self, list):
|
||||
# XXX We could do better here for very long lists
|
||||
# XXX Should really reject non-string non-buffers
|
||||
self._wbuf.extend([_f for _f in map(str, list) if _f])
|
||||
if (self._wbufsize <= 1 or
|
||||
self._get_wbuf_len() >= self._wbufsize):
|
||||
self.flush()
|
||||
|
||||
def _get_wbuf_len(self):
|
||||
buf_len = 0
|
||||
for x in self._wbuf:
|
||||
buf_len += len(x)
|
||||
return buf_len
|
||||
|
||||
def read(self, size=-1):
|
||||
data = self._rbuf
|
||||
if size < 0:
|
||||
# Read until EOF
|
||||
buffers = []
|
||||
if data:
|
||||
buffers.append(data)
|
||||
self._rbuf = ""
|
||||
if self._rbufsize <= 1:
|
||||
recv_size = self.default_bufsize
|
||||
else:
|
||||
recv_size = self._rbufsize
|
||||
while True:
|
||||
data = self._sock.recv(recv_size)
|
||||
if not data:
|
||||
break
|
||||
buffers.append(data)
|
||||
return "".join(buffers)
|
||||
else:
|
||||
# Read until size bytes or EOF seen, whichever comes first
|
||||
buf_len = len(data)
|
||||
if buf_len >= size:
|
||||
self._rbuf = data[size:]
|
||||
return data[:size]
|
||||
buffers = []
|
||||
if data:
|
||||
buffers.append(data)
|
||||
self._rbuf = ""
|
||||
while True:
|
||||
left = size - buf_len
|
||||
recv_size = max(self._rbufsize, left)
|
||||
data = self._sock.recv(recv_size)
|
||||
if not data:
|
||||
break
|
||||
buffers.append(data)
|
||||
n = len(data)
|
||||
if n >= left:
|
||||
self._rbuf = data[left:]
|
||||
buffers[-1] = data[:left]
|
||||
break
|
||||
buf_len += n
|
||||
return "".join(buffers)
|
||||
|
||||
def readline(self, size=-1):
|
||||
data = self._rbuf
|
||||
if size < 0:
|
||||
# Read until \n or EOF, whichever comes first
|
||||
if self._rbufsize <= 1:
|
||||
# Speed up unbuffered case
|
||||
assert data == ""
|
||||
buffers = []
|
||||
recv = self._sock.recv
|
||||
while data != "\n":
|
||||
data = recv(1)
|
||||
if not data:
|
||||
break
|
||||
buffers.append(data)
|
||||
return "".join(buffers)
|
||||
nl = data.find('\n')
|
||||
if nl >= 0:
|
||||
nl += 1
|
||||
self._rbuf = data[nl:]
|
||||
return data[:nl]
|
||||
buffers = []
|
||||
if data:
|
||||
buffers.append(data)
|
||||
self._rbuf = ""
|
||||
while True:
|
||||
data = self._sock.recv(self._rbufsize)
|
||||
if not data:
|
||||
break
|
||||
buffers.append(data)
|
||||
nl = data.find('\n')
|
||||
if nl >= 0:
|
||||
nl += 1
|
||||
self._rbuf = data[nl:]
|
||||
buffers[-1] = data[:nl]
|
||||
break
|
||||
return "".join(buffers)
|
||||
else:
|
||||
# Read until size bytes or \n or EOF seen, whichever comes first
|
||||
nl = data.find('\n', 0, size)
|
||||
if nl >= 0:
|
||||
nl += 1
|
||||
self._rbuf = data[nl:]
|
||||
return data[:nl]
|
||||
buf_len = len(data)
|
||||
if buf_len >= size:
|
||||
self._rbuf = data[size:]
|
||||
return data[:size]
|
||||
buffers = []
|
||||
if data:
|
||||
buffers.append(data)
|
||||
self._rbuf = ""
|
||||
while True:
|
||||
data = self._sock.recv(self._rbufsize)
|
||||
if not data:
|
||||
break
|
||||
buffers.append(data)
|
||||
left = size - buf_len
|
||||
nl = data.find('\n', 0, left)
|
||||
if nl >= 0:
|
||||
nl += 1
|
||||
self._rbuf = data[nl:]
|
||||
buffers[-1] = data[:nl]
|
||||
break
|
||||
n = len(data)
|
||||
if n >= left:
|
||||
self._rbuf = data[left:]
|
||||
buffers[-1] = data[:left]
|
||||
break
|
||||
buf_len += n
|
||||
return "".join(buffers)
|
||||
|
||||
def readlines(self, sizehint=0):
|
||||
total = 0
|
||||
list = []
|
||||
while True:
|
||||
line = self.readline()
|
||||
if not line:
|
||||
break
|
||||
list.append(line)
|
||||
total += len(line)
|
||||
if sizehint and total >= sizehint:
|
||||
break
|
||||
return list
|
||||
|
||||
# Iterator protocols
|
||||
|
||||
def __iter__(self):
|
||||
return self
|
||||
|
||||
def __next__(self):
|
||||
line = self.readline()
|
||||
if not line:
|
||||
raise StopIteration
|
||||
return line
|
||||
@@ -1,159 +0,0 @@
|
||||
"""Class for setting handshake parameters."""
|
||||
|
||||
from .constants import CertificateType
|
||||
from .utils import cryptomath
|
||||
from .utils import cipherfactory
|
||||
|
||||
class HandshakeSettings:
|
||||
"""This class encapsulates various parameters that can be used with
|
||||
a TLS handshake.
|
||||
@sort: minKeySize, maxKeySize, cipherNames, certificateTypes,
|
||||
minVersion, maxVersion
|
||||
|
||||
@type minKeySize: int
|
||||
@ivar minKeySize: The minimum bit length for asymmetric keys.
|
||||
|
||||
If the other party tries to use SRP, RSA, or Diffie-Hellman
|
||||
parameters smaller than this length, an alert will be
|
||||
signalled. The default is 1023.
|
||||
|
||||
@type maxKeySize: int
|
||||
@ivar maxKeySize: The maximum bit length for asymmetric keys.
|
||||
|
||||
If the other party tries to use SRP, RSA, or Diffie-Hellman
|
||||
parameters larger than this length, an alert will be signalled.
|
||||
The default is 8193.
|
||||
|
||||
@type cipherNames: list
|
||||
@ivar cipherNames: The allowed ciphers, in order of preference.
|
||||
|
||||
The allowed values in this list are 'aes256', 'aes128', '3des', and
|
||||
'rc4'. If these settings are used with a client handshake, they
|
||||
determine the order of the ciphersuites offered in the ClientHello
|
||||
message.
|
||||
|
||||
If these settings are used with a server handshake, the server will
|
||||
choose whichever ciphersuite matches the earliest entry in this
|
||||
list.
|
||||
|
||||
NOTE: If '3des' is used in this list, but TLS Lite can't find an
|
||||
add-on library that supports 3DES, then '3des' will be silently
|
||||
removed.
|
||||
|
||||
The default value is ['aes256', 'aes128', '3des', 'rc4'].
|
||||
|
||||
@type certificateTypes: list
|
||||
@ivar certificateTypes: The allowed certificate types, in order of
|
||||
preference.
|
||||
|
||||
The allowed values in this list are 'x509' and 'cryptoID'. This
|
||||
list is only used with a client handshake. The client will
|
||||
advertise to the server which certificate types are supported, and
|
||||
will check that the server uses one of the appropriate types.
|
||||
|
||||
NOTE: If 'cryptoID' is used in this list, but cryptoIDlib is not
|
||||
installed, then 'cryptoID' will be silently removed.
|
||||
|
||||
@type minVersion: tuple
|
||||
@ivar minVersion: The minimum allowed SSL/TLS version.
|
||||
|
||||
This variable can be set to (3,0) for SSL 3.0, (3,1) for
|
||||
TLS 1.0, or (3,2) for TLS 1.1. If the other party wishes to
|
||||
use a lower version, a protocol_version alert will be signalled.
|
||||
The default is (3,0).
|
||||
|
||||
@type maxVersion: tuple
|
||||
@ivar maxVersion: The maximum allowed SSL/TLS version.
|
||||
|
||||
This variable can be set to (3,0) for SSL 3.0, (3,1) for
|
||||
TLS 1.0, or (3,2) for TLS 1.1. If the other party wishes to
|
||||
use a higher version, a protocol_version alert will be signalled.
|
||||
The default is (3,2). (WARNING: Some servers may (improperly)
|
||||
reject clients which offer support for TLS 1.1. In this case,
|
||||
try lowering maxVersion to (3,1)).
|
||||
"""
|
||||
def __init__(self):
|
||||
self.minKeySize = 1023
|
||||
self.maxKeySize = 8193
|
||||
self.cipherNames = ["aes256", "aes128", "3des", "rc4"]
|
||||
self.cipherImplementations = ["cryptlib", "openssl", "pycrypto",
|
||||
"python"]
|
||||
self.certificateTypes = ["x509", "cryptoID"]
|
||||
self.minVersion = (3,0)
|
||||
self.maxVersion = (3,2)
|
||||
|
||||
#Filters out options that are not supported
|
||||
def _filter(self):
|
||||
other = HandshakeSettings()
|
||||
other.minKeySize = self.minKeySize
|
||||
other.maxKeySize = self.maxKeySize
|
||||
other.cipherNames = self.cipherNames
|
||||
other.cipherImplementations = self.cipherImplementations
|
||||
other.certificateTypes = self.certificateTypes
|
||||
other.minVersion = self.minVersion
|
||||
other.maxVersion = self.maxVersion
|
||||
|
||||
if not cipherfactory.tripleDESPresent:
|
||||
other.cipherNames = [e for e in self.cipherNames if e != "3des"]
|
||||
if len(other.cipherNames)==0:
|
||||
raise ValueError("No supported ciphers")
|
||||
|
||||
try:
|
||||
import cryptoIDlib
|
||||
except ImportError:
|
||||
other.certificateTypes = [e for e in self.certificateTypes \
|
||||
if e != "cryptoID"]
|
||||
if len(other.certificateTypes)==0:
|
||||
raise ValueError("No supported certificate types")
|
||||
|
||||
if not cryptomath.cryptlibpyLoaded:
|
||||
other.cipherImplementations = [e for e in \
|
||||
self.cipherImplementations if e != "cryptlib"]
|
||||
if not cryptomath.m2cryptoLoaded:
|
||||
other.cipherImplementations = [e for e in \
|
||||
other.cipherImplementations if e != "openssl"]
|
||||
if not cryptomath.pycryptoLoaded:
|
||||
other.cipherImplementations = [e for e in \
|
||||
other.cipherImplementations if e != "pycrypto"]
|
||||
if len(other.cipherImplementations)==0:
|
||||
raise ValueError("No supported cipher implementations")
|
||||
|
||||
if other.minKeySize<512:
|
||||
raise ValueError("minKeySize too small")
|
||||
if other.minKeySize>16384:
|
||||
raise ValueError("minKeySize too large")
|
||||
if other.maxKeySize<512:
|
||||
raise ValueError("maxKeySize too small")
|
||||
if other.maxKeySize>16384:
|
||||
raise ValueError("maxKeySize too large")
|
||||
for s in other.cipherNames:
|
||||
if s not in ("aes256", "aes128", "rc4", "3des"):
|
||||
raise ValueError("Unknown cipher name: '%s'" % s)
|
||||
for s in other.cipherImplementations:
|
||||
if s not in ("cryptlib", "openssl", "python", "pycrypto"):
|
||||
raise ValueError("Unknown cipher implementation: '%s'" % s)
|
||||
for s in other.certificateTypes:
|
||||
if s not in ("x509", "cryptoID"):
|
||||
raise ValueError("Unknown certificate type: '%s'" % s)
|
||||
|
||||
if other.minVersion > other.maxVersion:
|
||||
raise ValueError("Versions set incorrectly")
|
||||
|
||||
if not other.minVersion in ((3,0), (3,1), (3,2)):
|
||||
raise ValueError("minVersion set incorrectly")
|
||||
|
||||
if not other.maxVersion in ((3,0), (3,1), (3,2)):
|
||||
raise ValueError("maxVersion set incorrectly")
|
||||
|
||||
return other
|
||||
|
||||
def _getCertificateTypes(self):
|
||||
l = []
|
||||
for ct in self.certificateTypes:
|
||||
if ct == "x509":
|
||||
l.append(CertificateType.x509)
|
||||
elif ct == "cryptoID":
|
||||
l.append(CertificateType.cryptoID)
|
||||
else:
|
||||
raise AssertionError()
|
||||
return l
|
||||
@@ -1,131 +0,0 @@
|
||||
"""Class representing a TLS session."""
|
||||
|
||||
from .utils.compat import *
|
||||
from .mathtls import *
|
||||
from .constants import *
|
||||
|
||||
class Session:
|
||||
"""
|
||||
This class represents a TLS session.
|
||||
|
||||
TLS distinguishes between connections and sessions. A new
|
||||
handshake creates both a connection and a session. Data is
|
||||
transmitted over the connection.
|
||||
|
||||
The session contains a more permanent record of the handshake. The
|
||||
session can be inspected to determine handshake results. The
|
||||
session can also be used to create a new connection through
|
||||
"session resumption". If the client and server both support this,
|
||||
they can create a new connection based on an old session without
|
||||
the overhead of a full handshake.
|
||||
|
||||
The session for a L{tlslite.TLSConnection.TLSConnection} can be
|
||||
retrieved from the connection's 'session' attribute.
|
||||
|
||||
@type srpUsername: str
|
||||
@ivar srpUsername: The client's SRP username (or None).
|
||||
|
||||
@type sharedKeyUsername: str
|
||||
@ivar sharedKeyUsername: The client's shared-key username (or
|
||||
None).
|
||||
|
||||
@type clientCertChain: L{tlslite.X509CertChain.X509CertChain} or
|
||||
L{cryptoIDlib.CertChain.CertChain}
|
||||
@ivar clientCertChain: The client's certificate chain (or None).
|
||||
|
||||
@type serverCertChain: L{tlslite.X509CertChain.X509CertChain} or
|
||||
L{cryptoIDlib.CertChain.CertChain}
|
||||
@ivar serverCertChain: The server's certificate chain (or None).
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
self.masterSecret = createByteArraySequence([])
|
||||
self.sessionID = createByteArraySequence([])
|
||||
self.cipherSuite = 0
|
||||
self.srpUsername = None
|
||||
self.sharedKeyUsername = None
|
||||
self.clientCertChain = None
|
||||
self.serverCertChain = None
|
||||
self.resumable = False
|
||||
self.sharedKey = False
|
||||
|
||||
def _clone(self):
|
||||
other = Session()
|
||||
other.masterSecret = self.masterSecret
|
||||
other.sessionID = self.sessionID
|
||||
other.cipherSuite = self.cipherSuite
|
||||
other.srpUsername = self.srpUsername
|
||||
other.sharedKeyUsername = self.sharedKeyUsername
|
||||
other.clientCertChain = self.clientCertChain
|
||||
other.serverCertChain = self.serverCertChain
|
||||
other.resumable = self.resumable
|
||||
other.sharedKey = self.sharedKey
|
||||
return other
|
||||
|
||||
def _calcMasterSecret(self, version, premasterSecret, clientRandom,
|
||||
serverRandom):
|
||||
if version == (3,0):
|
||||
self.masterSecret = PRF_SSL(premasterSecret,
|
||||
concatArrays(clientRandom, serverRandom), 48)
|
||||
elif version in ((3,1), (3,2)):
|
||||
self.masterSecret = PRF(premasterSecret, "master secret",
|
||||
concatArrays(clientRandom, serverRandom), 48)
|
||||
else:
|
||||
raise AssertionError()
|
||||
|
||||
def valid(self):
|
||||
"""If this session can be used for session resumption.
|
||||
|
||||
@rtype: bool
|
||||
@return: If this session can be used for session resumption.
|
||||
"""
|
||||
return self.resumable or self.sharedKey
|
||||
|
||||
def _setResumable(self, boolean):
|
||||
#Only let it be set if this isn't a shared key
|
||||
if not self.sharedKey:
|
||||
#Only let it be set to True if the sessionID is non-null
|
||||
if (not boolean) or (boolean and self.sessionID):
|
||||
self.resumable = boolean
|
||||
|
||||
def getCipherName(self):
|
||||
"""Get the name of the cipher used with this connection.
|
||||
|
||||
@rtype: str
|
||||
@return: The name of the cipher used with this connection.
|
||||
Either 'aes128', 'aes256', 'rc4', or '3des'.
|
||||
"""
|
||||
if self.cipherSuite in CipherSuite.aes128Suites:
|
||||
return "aes128"
|
||||
elif self.cipherSuite in CipherSuite.aes256Suites:
|
||||
return "aes256"
|
||||
elif self.cipherSuite in CipherSuite.rc4Suites:
|
||||
return "rc4"
|
||||
elif self.cipherSuite in CipherSuite.tripleDESSuites:
|
||||
return "3des"
|
||||
else:
|
||||
return None
|
||||
|
||||
def _createSharedKey(self, sharedKeyUsername, sharedKey):
|
||||
if len(sharedKeyUsername)>16:
|
||||
raise ValueError()
|
||||
if len(sharedKey)>47:
|
||||
raise ValueError()
|
||||
|
||||
self.sharedKeyUsername = sharedKeyUsername
|
||||
|
||||
self.sessionID = createByteArrayZeros(16)
|
||||
for x in range(len(sharedKeyUsername)):
|
||||
self.sessionID[x] = ord(sharedKeyUsername[x])
|
||||
|
||||
premasterSecret = createByteArrayZeros(48)
|
||||
sharedKey = chr(len(sharedKey)) + sharedKey
|
||||
for x in range(48):
|
||||
premasterSecret[x] = ord(sharedKey[x % len(sharedKey)])
|
||||
|
||||
self.masterSecret = PRF(premasterSecret, "shared secret",
|
||||
createByteArraySequence([]), 48)
|
||||
self.sharedKey = True
|
||||
return self
|
||||
|
||||
|
||||
@@ -1,103 +0,0 @@
|
||||
"""Class for caching TLS sessions."""
|
||||
|
||||
import _thread
|
||||
import time
|
||||
|
||||
class SessionCache:
|
||||
"""This class is used by the server to cache TLS sessions.
|
||||
|
||||
Caching sessions allows the client to use TLS session resumption
|
||||
and avoid the expense of a full handshake. To use this class,
|
||||
simply pass a SessionCache instance into the server handshake
|
||||
function.
|
||||
|
||||
This class is thread-safe.
|
||||
"""
|
||||
|
||||
#References to these instances
|
||||
#are also held by the caller, who may change the 'resumable'
|
||||
#flag, so the SessionCache must return the same instances
|
||||
#it was passed in.
|
||||
|
||||
def __init__(self, maxEntries=10000, maxAge=14400):
|
||||
"""Create a new SessionCache.
|
||||
|
||||
@type maxEntries: int
|
||||
@param maxEntries: The maximum size of the cache. When this
|
||||
limit is reached, the oldest sessions will be deleted as
|
||||
necessary to make room for new ones. The default is 10000.
|
||||
|
||||
@type maxAge: int
|
||||
@param maxAge: The number of seconds before a session expires
|
||||
from the cache. The default is 14400 (i.e. 4 hours)."""
|
||||
|
||||
self.lock = _thread.allocate_lock()
|
||||
|
||||
# Maps sessionIDs to sessions
|
||||
self.entriesDict = {}
|
||||
|
||||
#Circular list of (sessionID, timestamp) pairs
|
||||
self.entriesList = [(None,None)] * maxEntries
|
||||
|
||||
self.firstIndex = 0
|
||||
self.lastIndex = 0
|
||||
self.maxAge = maxAge
|
||||
|
||||
def __getitem__(self, sessionID):
|
||||
self.lock.acquire()
|
||||
try:
|
||||
self._purge() #Delete old items, so we're assured of a new one
|
||||
session = self.entriesDict[sessionID]
|
||||
|
||||
#When we add sessions they're resumable, but it's possible
|
||||
#for the session to be invalidated later on (if a fatal alert
|
||||
#is returned), so we have to check for resumability before
|
||||
#returning the session.
|
||||
|
||||
if session.valid():
|
||||
return session
|
||||
else:
|
||||
raise KeyError()
|
||||
finally:
|
||||
self.lock.release()
|
||||
|
||||
|
||||
def __setitem__(self, sessionID, session):
|
||||
self.lock.acquire()
|
||||
try:
|
||||
#Add the new element
|
||||
self.entriesDict[sessionID] = session
|
||||
self.entriesList[self.lastIndex] = (sessionID, time.time())
|
||||
self.lastIndex = (self.lastIndex+1) % len(self.entriesList)
|
||||
|
||||
#If the cache is full, we delete the oldest element to make an
|
||||
#empty space
|
||||
if self.lastIndex == self.firstIndex:
|
||||
del(self.entriesDict[self.entriesList[self.firstIndex][0]])
|
||||
self.firstIndex = (self.firstIndex+1) % len(self.entriesList)
|
||||
finally:
|
||||
self.lock.release()
|
||||
|
||||
#Delete expired items
|
||||
def _purge(self):
|
||||
currentTime = time.time()
|
||||
|
||||
#Search through the circular list, deleting expired elements until
|
||||
#we reach a non-expired element. Since elements in list are
|
||||
#ordered in time, we can break once we reach the first non-expired
|
||||
#element
|
||||
index = self.firstIndex
|
||||
while index != self.lastIndex:
|
||||
if currentTime - self.entriesList[index][1] > self.maxAge:
|
||||
del(self.entriesDict[self.entriesList[index][0]])
|
||||
index = (index+1) % len(self.entriesList)
|
||||
else:
|
||||
break
|
||||
self.firstIndex = index
|
||||
|
||||
def _test():
|
||||
import doctest, SessionCache
|
||||
return doctest.testmod(SessionCache)
|
||||
|
||||
if __name__ == "__main__":
|
||||
_test()
|
||||
@@ -1,58 +0,0 @@
|
||||
"""Class for storing shared keys."""
|
||||
|
||||
from .utils.cryptomath import *
|
||||
from .utils.compat import *
|
||||
from .mathtls import *
|
||||
from .Session import Session
|
||||
from .BaseDB import BaseDB
|
||||
|
||||
class SharedKeyDB(BaseDB):
|
||||
"""This class represent an in-memory or on-disk database of shared
|
||||
keys.
|
||||
|
||||
A SharedKeyDB can be passed to a server handshake function to
|
||||
authenticate a client based on one of the shared keys.
|
||||
|
||||
This class is thread-safe.
|
||||
"""
|
||||
|
||||
def __init__(self, filename=None):
|
||||
"""Create a new SharedKeyDB.
|
||||
|
||||
@type filename: str
|
||||
@param filename: Filename for an on-disk database, or None for
|
||||
an in-memory database. If the filename already exists, follow
|
||||
this with a call to open(). To create a new on-disk database,
|
||||
follow this with a call to create().
|
||||
"""
|
||||
BaseDB.__init__(self, filename, "shared key")
|
||||
|
||||
def _getItem(self, username, valueStr):
|
||||
session = Session()
|
||||
session._createSharedKey(username, valueStr)
|
||||
return session
|
||||
|
||||
def __setitem__(self, username, sharedKey):
|
||||
"""Add a shared key to the database.
|
||||
|
||||
@type username: str
|
||||
@param username: The username to associate the shared key with.
|
||||
Must be less than or equal to 16 characters in length, and must
|
||||
not already be in the database.
|
||||
|
||||
@type sharedKey: str
|
||||
@param sharedKey: The shared key to add. Must be less than 48
|
||||
characters in length.
|
||||
"""
|
||||
BaseDB.__setitem__(self, username, sharedKey)
|
||||
|
||||
def _setItem(self, username, value):
|
||||
if len(username)>16:
|
||||
raise ValueError("username too long")
|
||||
if len(value)>=48:
|
||||
raise ValueError("shared key too long")
|
||||
return value
|
||||
|
||||
def _checkItem(self, value, username, param):
|
||||
newSession = self._getItem(username, param)
|
||||
return value.masterSecret == newSession.masterSecret
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -1,90 +0,0 @@
|
||||
"""Class for storing SRP password verifiers."""
|
||||
|
||||
from .utils.cryptomath import *
|
||||
from .utils.compat import *
|
||||
from . import mathtls
|
||||
from .BaseDB import BaseDB
|
||||
|
||||
class VerifierDB(BaseDB):
|
||||
"""This class represent an in-memory or on-disk database of SRP
|
||||
password verifiers.
|
||||
|
||||
A VerifierDB can be passed to a server handshake to authenticate
|
||||
a client based on one of the verifiers.
|
||||
|
||||
This class is thread-safe.
|
||||
"""
|
||||
def __init__(self, filename=None):
|
||||
"""Create a new VerifierDB instance.
|
||||
|
||||
@type filename: str
|
||||
@param filename: Filename for an on-disk database, or None for
|
||||
an in-memory database. If the filename already exists, follow
|
||||
this with a call to open(). To create a new on-disk database,
|
||||
follow this with a call to create().
|
||||
"""
|
||||
BaseDB.__init__(self, filename, "verifier")
|
||||
|
||||
def _getItem(self, username, valueStr):
|
||||
(N, g, salt, verifier) = valueStr.split(" ")
|
||||
N = base64ToNumber(N)
|
||||
g = base64ToNumber(g)
|
||||
salt = base64ToString(salt)
|
||||
verifier = base64ToNumber(verifier)
|
||||
return (N, g, salt, verifier)
|
||||
|
||||
def __setitem__(self, username, verifierEntry):
|
||||
"""Add a verifier entry to the database.
|
||||
|
||||
@type username: str
|
||||
@param username: The username to associate the verifier with.
|
||||
Must be less than 256 characters in length. Must not already
|
||||
be in the database.
|
||||
|
||||
@type verifierEntry: tuple
|
||||
@param verifierEntry: The verifier entry to add. Use
|
||||
L{tlslite.VerifierDB.VerifierDB.makeVerifier} to create a
|
||||
verifier entry.
|
||||
"""
|
||||
BaseDB.__setitem__(self, username, verifierEntry)
|
||||
|
||||
|
||||
def _setItem(self, username, value):
|
||||
if len(username)>=256:
|
||||
raise ValueError("username too long")
|
||||
N, g, salt, verifier = value
|
||||
N = numberToBase64(N)
|
||||
g = numberToBase64(g)
|
||||
salt = stringToBase64(salt)
|
||||
verifier = numberToBase64(verifier)
|
||||
valueStr = " ".join( (N, g, salt, verifier) )
|
||||
return valueStr
|
||||
|
||||
def _checkItem(self, value, username, param):
|
||||
(N, g, salt, verifier) = value
|
||||
x = mathtls.makeX(salt, username, param)
|
||||
v = powMod(g, x, N)
|
||||
return (verifier == v)
|
||||
|
||||
|
||||
def makeVerifier(username, password, bits):
|
||||
"""Create a verifier entry which can be stored in a VerifierDB.
|
||||
|
||||
@type username: str
|
||||
@param username: The username for this verifier. Must be less
|
||||
than 256 characters in length.
|
||||
|
||||
@type password: str
|
||||
@param password: The password for this verifier.
|
||||
|
||||
@type bits: int
|
||||
@param bits: This values specifies which SRP group parameters
|
||||
to use. It must be one of (1024, 1536, 2048, 3072, 4096, 6144,
|
||||
8192). Larger values are more secure but slower. 2048 is a
|
||||
good compromise between safety and speed.
|
||||
|
||||
@rtype: tuple
|
||||
@return: A tuple which may be stored in a VerifierDB.
|
||||
"""
|
||||
return mathtls.makeVerifier(username, password, bits)
|
||||
makeVerifier = staticmethod(makeVerifier)
|
||||
@@ -1,133 +0,0 @@
|
||||
"""Class representing an X.509 certificate."""
|
||||
|
||||
from .utils.ASN1Parser import ASN1Parser
|
||||
from .utils.cryptomath import *
|
||||
from .utils.keyfactory import _createPublicRSAKey
|
||||
|
||||
|
||||
class X509:
|
||||
"""This class represents an X.509 certificate.
|
||||
|
||||
@type bytes: L{array.array} of unsigned bytes
|
||||
@ivar bytes: The DER-encoded ASN.1 certificate
|
||||
|
||||
@type publicKey: L{tlslite.utils.RSAKey.RSAKey}
|
||||
@ivar publicKey: The subject public key from the certificate.
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
self.bytes = createByteArraySequence([])
|
||||
self.publicKey = None
|
||||
|
||||
def parse(self, s):
|
||||
"""Parse a PEM-encoded X.509 certificate.
|
||||
|
||||
@type s: str
|
||||
@param s: A PEM-encoded X.509 certificate (i.e. a base64-encoded
|
||||
certificate wrapped with "-----BEGIN CERTIFICATE-----" and
|
||||
"-----END CERTIFICATE-----" tags).
|
||||
"""
|
||||
|
||||
start = s.find("-----BEGIN CERTIFICATE-----")
|
||||
end = s.find("-----END CERTIFICATE-----")
|
||||
if start == -1:
|
||||
raise SyntaxError("Missing PEM prefix")
|
||||
if end == -1:
|
||||
raise SyntaxError("Missing PEM postfix")
|
||||
s = s[start+len("-----BEGIN CERTIFICATE-----") : end]
|
||||
|
||||
bytes = base64ToBytes(s)
|
||||
self.parseBinary(bytes)
|
||||
return self
|
||||
|
||||
def parseBinary(self, bytes):
|
||||
"""Parse a DER-encoded X.509 certificate.
|
||||
|
||||
@type bytes: str or L{array.array} of unsigned bytes
|
||||
@param bytes: A DER-encoded X.509 certificate.
|
||||
"""
|
||||
|
||||
if isinstance(bytes, type("")):
|
||||
bytes = stringToBytes(bytes)
|
||||
|
||||
self.bytes = bytes
|
||||
p = ASN1Parser(bytes)
|
||||
|
||||
#Get the tbsCertificate
|
||||
tbsCertificateP = p.getChild(0)
|
||||
|
||||
#Is the optional version field present?
|
||||
#This determines which index the key is at.
|
||||
if tbsCertificateP.value[0]==0xA0:
|
||||
subjectPublicKeyInfoIndex = 6
|
||||
else:
|
||||
subjectPublicKeyInfoIndex = 5
|
||||
|
||||
#Get the subjectPublicKeyInfo
|
||||
subjectPublicKeyInfoP = tbsCertificateP.getChild(\
|
||||
subjectPublicKeyInfoIndex)
|
||||
|
||||
#Get the algorithm
|
||||
algorithmP = subjectPublicKeyInfoP.getChild(0)
|
||||
rsaOID = algorithmP.value
|
||||
if list(rsaOID) != [6, 9, 42, 134, 72, 134, 247, 13, 1, 1, 1, 5, 0]:
|
||||
raise SyntaxError("Unrecognized AlgorithmIdentifier")
|
||||
|
||||
#Get the subjectPublicKey
|
||||
subjectPublicKeyP = subjectPublicKeyInfoP.getChild(1)
|
||||
|
||||
#Adjust for BIT STRING encapsulation
|
||||
if (subjectPublicKeyP.value[0] !=0):
|
||||
raise SyntaxError()
|
||||
subjectPublicKeyP = ASN1Parser(subjectPublicKeyP.value[1:])
|
||||
|
||||
#Get the modulus and exponent
|
||||
modulusP = subjectPublicKeyP.getChild(0)
|
||||
publicExponentP = subjectPublicKeyP.getChild(1)
|
||||
|
||||
#Decode them into numbers
|
||||
n = bytesToNumber(modulusP.value)
|
||||
e = bytesToNumber(publicExponentP.value)
|
||||
|
||||
#Create a public key instance
|
||||
self.publicKey = _createPublicRSAKey(n, e)
|
||||
|
||||
def getFingerprint(self):
|
||||
"""Get the hex-encoded fingerprint of this certificate.
|
||||
|
||||
@rtype: str
|
||||
@return: A hex-encoded fingerprint.
|
||||
"""
|
||||
return sha.sha(self.bytes).hexdigest()
|
||||
|
||||
def getCommonName(self):
|
||||
"""Get the Subject's Common Name from the certificate.
|
||||
|
||||
The cryptlib_py module must be installed in order to use this
|
||||
function.
|
||||
|
||||
@rtype: str or None
|
||||
@return: The CN component of the certificate's subject DN, if
|
||||
present.
|
||||
"""
|
||||
import cryptlib_py
|
||||
import array
|
||||
c = cryptlib_py.cryptImportCert(self.bytes, cryptlib_py.CRYPT_UNUSED)
|
||||
name = cryptlib_py.CRYPT_CERTINFO_COMMONNAME
|
||||
try:
|
||||
try:
|
||||
length = cryptlib_py.cryptGetAttributeString(c, name, None)
|
||||
returnVal = array.array('B', [0] * length)
|
||||
cryptlib_py.cryptGetAttributeString(c, name, returnVal)
|
||||
returnVal = returnVal.tostring()
|
||||
except cryptlib_py.CryptException as e:
|
||||
if e[0] == cryptlib_py.CRYPT_ERROR_NOTFOUND:
|
||||
returnVal = None
|
||||
return returnVal
|
||||
finally:
|
||||
cryptlib_py.cryptDestroyCert(c)
|
||||
|
||||
def writeBytes(self):
|
||||
return self.bytes
|
||||
|
||||
|
||||
@@ -1,181 +0,0 @@
|
||||
"""Class representing an X.509 certificate chain."""
|
||||
|
||||
from .utils import cryptomath
|
||||
|
||||
class X509CertChain:
|
||||
"""This class represents a chain of X.509 certificates.
|
||||
|
||||
@type x509List: list
|
||||
@ivar x509List: A list of L{tlslite.X509.X509} instances,
|
||||
starting with the end-entity certificate and with every
|
||||
subsequent certificate certifying the previous.
|
||||
"""
|
||||
|
||||
def __init__(self, x509List=None):
|
||||
"""Create a new X509CertChain.
|
||||
|
||||
@type x509List: list
|
||||
@param x509List: A list of L{tlslite.X509.X509} instances,
|
||||
starting with the end-entity certificate and with every
|
||||
subsequent certificate certifying the previous.
|
||||
"""
|
||||
if x509List:
|
||||
self.x509List = x509List
|
||||
else:
|
||||
self.x509List = []
|
||||
|
||||
def getNumCerts(self):
|
||||
"""Get the number of certificates in this chain.
|
||||
|
||||
@rtype: int
|
||||
"""
|
||||
return len(self.x509List)
|
||||
|
||||
def getEndEntityPublicKey(self):
|
||||
"""Get the public key from the end-entity certificate.
|
||||
|
||||
@rtype: L{tlslite.utils.RSAKey.RSAKey}
|
||||
"""
|
||||
if self.getNumCerts() == 0:
|
||||
raise AssertionError()
|
||||
return self.x509List[0].publicKey
|
||||
|
||||
def getFingerprint(self):
|
||||
"""Get the hex-encoded fingerprint of the end-entity certificate.
|
||||
|
||||
@rtype: str
|
||||
@return: A hex-encoded fingerprint.
|
||||
"""
|
||||
if self.getNumCerts() == 0:
|
||||
raise AssertionError()
|
||||
return self.x509List[0].getFingerprint()
|
||||
|
||||
def getCommonName(self):
|
||||
"""Get the Subject's Common Name from the end-entity certificate.
|
||||
|
||||
The cryptlib_py module must be installed in order to use this
|
||||
function.
|
||||
|
||||
@rtype: str or None
|
||||
@return: The CN component of the certificate's subject DN, if
|
||||
present.
|
||||
"""
|
||||
if self.getNumCerts() == 0:
|
||||
raise AssertionError()
|
||||
return self.x509List[0].getCommonName()
|
||||
|
||||
def validate(self, x509TrustList):
|
||||
"""Check the validity of the certificate chain.
|
||||
|
||||
This checks that every certificate in the chain validates with
|
||||
the subsequent one, until some certificate validates with (or
|
||||
is identical to) one of the passed-in root certificates.
|
||||
|
||||
The cryptlib_py module must be installed in order to use this
|
||||
function.
|
||||
|
||||
@type x509TrustList: list of L{tlslite.X509.X509}
|
||||
@param x509TrustList: A list of trusted root certificates. The
|
||||
certificate chain must extend to one of these certificates to
|
||||
be considered valid.
|
||||
"""
|
||||
|
||||
import cryptlib_py
|
||||
c1 = None
|
||||
c2 = None
|
||||
lastC = None
|
||||
rootC = None
|
||||
|
||||
try:
|
||||
rootFingerprints = [c.getFingerprint() for c in x509TrustList]
|
||||
|
||||
#Check that every certificate in the chain validates with the
|
||||
#next one
|
||||
for cert1, cert2 in zip(self.x509List, self.x509List[1:]):
|
||||
|
||||
#If we come upon a root certificate, we're done.
|
||||
if cert1.getFingerprint() in rootFingerprints:
|
||||
return True
|
||||
|
||||
c1 = cryptlib_py.cryptImportCert(cert1.writeBytes(),
|
||||
cryptlib_py.CRYPT_UNUSED)
|
||||
c2 = cryptlib_py.cryptImportCert(cert2.writeBytes(),
|
||||
cryptlib_py.CRYPT_UNUSED)
|
||||
try:
|
||||
cryptlib_py.cryptCheckCert(c1, c2)
|
||||
except:
|
||||
return False
|
||||
cryptlib_py.cryptDestroyCert(c1)
|
||||
c1 = None
|
||||
cryptlib_py.cryptDestroyCert(c2)
|
||||
c2 = None
|
||||
|
||||
#If the last certificate is one of the root certificates, we're
|
||||
#done.
|
||||
if self.x509List[-1].getFingerprint() in rootFingerprints:
|
||||
return True
|
||||
|
||||
#Otherwise, find a root certificate that the last certificate
|
||||
#chains to, and validate them.
|
||||
lastC = cryptlib_py.cryptImportCert(self.x509List[-1].writeBytes(),
|
||||
cryptlib_py.CRYPT_UNUSED)
|
||||
for rootCert in x509TrustList:
|
||||
rootC = cryptlib_py.cryptImportCert(rootCert.writeBytes(),
|
||||
cryptlib_py.CRYPT_UNUSED)
|
||||
if self._checkChaining(lastC, rootC):
|
||||
try:
|
||||
cryptlib_py.cryptCheckCert(lastC, rootC)
|
||||
return True
|
||||
except:
|
||||
return False
|
||||
return False
|
||||
finally:
|
||||
if not (c1 is None):
|
||||
cryptlib_py.cryptDestroyCert(c1)
|
||||
if not (c2 is None):
|
||||
cryptlib_py.cryptDestroyCert(c2)
|
||||
if not (lastC is None):
|
||||
cryptlib_py.cryptDestroyCert(lastC)
|
||||
if not (rootC is None):
|
||||
cryptlib_py.cryptDestroyCert(rootC)
|
||||
|
||||
|
||||
|
||||
def _checkChaining(self, lastC, rootC):
|
||||
import cryptlib_py
|
||||
import array
|
||||
def compareNames(name):
|
||||
try:
|
||||
length = cryptlib_py.cryptGetAttributeString(lastC, name, None)
|
||||
lastName = array.array('B', [0] * length)
|
||||
cryptlib_py.cryptGetAttributeString(lastC, name, lastName)
|
||||
lastName = lastName.tostring()
|
||||
except cryptlib_py.CryptException as e:
|
||||
if e[0] == cryptlib_py.CRYPT_ERROR_NOTFOUND:
|
||||
lastName = None
|
||||
try:
|
||||
length = cryptlib_py.cryptGetAttributeString(rootC, name, None)
|
||||
rootName = array.array('B', [0] * length)
|
||||
cryptlib_py.cryptGetAttributeString(rootC, name, rootName)
|
||||
rootName = rootName.tostring()
|
||||
except cryptlib_py.CryptException as e:
|
||||
if e[0] == cryptlib_py.CRYPT_ERROR_NOTFOUND:
|
||||
rootName = None
|
||||
|
||||
return lastName == rootName
|
||||
|
||||
cryptlib_py.cryptSetAttribute(lastC,
|
||||
cryptlib_py.CRYPT_CERTINFO_ISSUERNAME,
|
||||
cryptlib_py.CRYPT_UNUSED)
|
||||
|
||||
if not compareNames(cryptlib_py.CRYPT_CERTINFO_COUNTRYNAME):
|
||||
return False
|
||||
if not compareNames(cryptlib_py.CRYPT_CERTINFO_LOCALITYNAME):
|
||||
return False
|
||||
if not compareNames(cryptlib_py.CRYPT_CERTINFO_ORGANIZATIONNAME):
|
||||
return False
|
||||
if not compareNames(cryptlib_py.CRYPT_CERTINFO_ORGANIZATIONALUNITNAME):
|
||||
return False
|
||||
if not compareNames(cryptlib_py.CRYPT_CERTINFO_COMMONNAME):
|
||||
return False
|
||||
return True
|
||||
@@ -1,39 +0,0 @@
|
||||
"""
|
||||
TLS Lite is a free python library that implements SSL v3, TLS v1, and
|
||||
TLS v1.1. TLS Lite supports non-traditional authentication methods
|
||||
such as SRP, shared keys, and cryptoIDs, in addition to X.509
|
||||
certificates. TLS Lite is pure python, however it can access OpenSSL,
|
||||
cryptlib, pycrypto, and GMPY for faster crypto operations. TLS Lite
|
||||
integrates with httplib, xmlrpclib, poplib, imaplib, smtplib,
|
||||
SocketServer, asyncore, and Twisted.
|
||||
|
||||
To use, do::
|
||||
|
||||
from tlslite.api import *
|
||||
|
||||
Then use the L{tlslite.TLSConnection.TLSConnection} class with a socket,
|
||||
or use one of the integration classes in L{tlslite.integration}.
|
||||
|
||||
@version: 0.3.8
|
||||
"""
|
||||
__version__ = "0.3.8"
|
||||
|
||||
__all__ = ["api",
|
||||
"BaseDB",
|
||||
"Checker",
|
||||
"constants",
|
||||
"errors",
|
||||
"FileObject",
|
||||
"HandshakeSettings",
|
||||
"mathtls",
|
||||
"messages",
|
||||
"Session",
|
||||
"SessionCache",
|
||||
"SharedKeyDB",
|
||||
"TLSConnection",
|
||||
"TLSRecordLayer",
|
||||
"VerifierDB",
|
||||
"X509",
|
||||
"X509CertChain",
|
||||
"integration",
|
||||
"utils"]
|
||||
@@ -1,75 +0,0 @@
|
||||
"""Import this module for easy access to TLS Lite objects.
|
||||
|
||||
The TLS Lite API consists of classes, functions, and variables spread
|
||||
throughout this package. Instead of importing them individually with::
|
||||
|
||||
from tlslite.TLSConnection import TLSConnection
|
||||
from tlslite.HandshakeSettings import HandshakeSettings
|
||||
from tlslite.errors import *
|
||||
.
|
||||
.
|
||||
|
||||
It's easier to do::
|
||||
|
||||
from tlslite.api import *
|
||||
|
||||
This imports all the important objects (TLSConnection, Checker,
|
||||
HandshakeSettings, etc.) into the global namespace. In particular, it
|
||||
imports::
|
||||
|
||||
from constants import AlertLevel, AlertDescription, Fault
|
||||
from errors import *
|
||||
from Checker import Checker
|
||||
from HandshakeSettings import HandshakeSettings
|
||||
from Session import Session
|
||||
from SessionCache import SessionCache
|
||||
from SharedKeyDB import SharedKeyDB
|
||||
from TLSConnection import TLSConnection
|
||||
from VerifierDB import VerifierDB
|
||||
from X509 import X509
|
||||
from X509CertChain import X509CertChain
|
||||
|
||||
from integration.HTTPTLSConnection import HTTPTLSConnection
|
||||
from integration.POP3_TLS import POP3_TLS
|
||||
from integration.IMAP4_TLS import IMAP4_TLS
|
||||
from integration.SMTP_TLS import SMTP_TLS
|
||||
from integration.XMLRPCTransport import XMLRPCTransport
|
||||
from integration.TLSSocketServerMixIn import TLSSocketServerMixIn
|
||||
from integration.TLSAsyncDispatcherMixIn import TLSAsyncDispatcherMixIn
|
||||
from integration.TLSTwistedProtocolWrapper import TLSTwistedProtocolWrapper
|
||||
from utils.cryptomath import cryptlibpyLoaded, m2cryptoLoaded,
|
||||
gmpyLoaded, pycryptoLoaded, prngName
|
||||
from utils.keyfactory import generateRSAKey, parsePEMKey, parseXMLKey,
|
||||
parseAsPublicKey, parsePrivateKey
|
||||
"""
|
||||
|
||||
from .constants import AlertLevel, AlertDescription, Fault
|
||||
from .errors import *
|
||||
from .Checker import Checker
|
||||
from .HandshakeSettings import HandshakeSettings
|
||||
from .Session import Session
|
||||
from .SessionCache import SessionCache
|
||||
from .SharedKeyDB import SharedKeyDB
|
||||
from .TLSConnection import TLSConnection
|
||||
from .VerifierDB import VerifierDB
|
||||
from .X509 import X509
|
||||
from .X509CertChain import X509CertChain
|
||||
|
||||
from .integration.HTTPTLSConnection import HTTPTLSConnection
|
||||
from .integration.TLSSocketServerMixIn import TLSSocketServerMixIn
|
||||
from .integration.TLSAsyncDispatcherMixIn import TLSAsyncDispatcherMixIn
|
||||
from .integration.POP3_TLS import POP3_TLS
|
||||
from .integration.IMAP4_TLS import IMAP4_TLS
|
||||
from .integration.SMTP_TLS import SMTP_TLS
|
||||
from .integration.XMLRPCTransport import XMLRPCTransport
|
||||
try:
|
||||
import twisted
|
||||
del(twisted)
|
||||
from .integration.TLSTwistedProtocolWrapper import TLSTwistedProtocolWrapper
|
||||
except ImportError:
|
||||
pass
|
||||
|
||||
from .utils.cryptomath import cryptlibpyLoaded, m2cryptoLoaded, gmpyLoaded, \
|
||||
pycryptoLoaded, prngName
|
||||
from .utils.keyfactory import generateRSAKey, parsePEMKey, parseXMLKey, \
|
||||
parseAsPublicKey, parsePrivateKey
|
||||
@@ -1,225 +0,0 @@
|
||||
"""Constants used in various places."""
|
||||
|
||||
class CertificateType:
|
||||
x509 = 0
|
||||
openpgp = 1
|
||||
cryptoID = 2
|
||||
|
||||
class HandshakeType:
|
||||
hello_request = 0
|
||||
client_hello = 1
|
||||
server_hello = 2
|
||||
certificate = 11
|
||||
server_key_exchange = 12
|
||||
certificate_request = 13
|
||||
server_hello_done = 14
|
||||
certificate_verify = 15
|
||||
client_key_exchange = 16
|
||||
finished = 20
|
||||
|
||||
class ContentType:
|
||||
change_cipher_spec = 20
|
||||
alert = 21
|
||||
handshake = 22
|
||||
application_data = 23
|
||||
all = (20,21,22,23)
|
||||
|
||||
class AlertLevel:
|
||||
warning = 1
|
||||
fatal = 2
|
||||
|
||||
class AlertDescription:
|
||||
"""
|
||||
@cvar bad_record_mac: A TLS record failed to decrypt properly.
|
||||
|
||||
If this occurs during a shared-key or SRP handshake it most likely
|
||||
indicates a bad password. It may also indicate an implementation
|
||||
error, or some tampering with the data in transit.
|
||||
|
||||
This alert will be signalled by the server if the SRP password is bad. It
|
||||
may also be signalled by the server if the SRP username is unknown to the
|
||||
server, but it doesn't wish to reveal that fact.
|
||||
|
||||
This alert will be signalled by the client if the shared-key username is
|
||||
bad.
|
||||
|
||||
@cvar handshake_failure: A problem occurred while handshaking.
|
||||
|
||||
This typically indicates a lack of common ciphersuites between client and
|
||||
server, or some other disagreement (about SRP parameters or key sizes,
|
||||
for example).
|
||||
|
||||
@cvar protocol_version: The other party's SSL/TLS version was unacceptable.
|
||||
|
||||
This indicates that the client and server couldn't agree on which version
|
||||
of SSL or TLS to use.
|
||||
|
||||
@cvar user_canceled: The handshake is being cancelled for some reason.
|
||||
|
||||
"""
|
||||
|
||||
close_notify = 0
|
||||
unexpected_message = 10
|
||||
bad_record_mac = 20
|
||||
decryption_failed = 21
|
||||
record_overflow = 22
|
||||
decompression_failure = 30
|
||||
handshake_failure = 40
|
||||
no_certificate = 41 #SSLv3
|
||||
bad_certificate = 42
|
||||
unsupported_certificate = 43
|
||||
certificate_revoked = 44
|
||||
certificate_expired = 45
|
||||
certificate_unknown = 46
|
||||
illegal_parameter = 47
|
||||
unknown_ca = 48
|
||||
access_denied = 49
|
||||
decode_error = 50
|
||||
decrypt_error = 51
|
||||
export_restriction = 60
|
||||
protocol_version = 70
|
||||
insufficient_security = 71
|
||||
internal_error = 80
|
||||
user_canceled = 90
|
||||
no_renegotiation = 100
|
||||
unknown_srp_username = 120
|
||||
missing_srp_username = 121
|
||||
untrusted_srp_parameters = 122
|
||||
|
||||
class CipherSuite:
|
||||
TLS_SRP_SHA_WITH_3DES_EDE_CBC_SHA = 0x0050
|
||||
TLS_SRP_SHA_WITH_AES_128_CBC_SHA = 0x0053
|
||||
TLS_SRP_SHA_WITH_AES_256_CBC_SHA = 0x0056
|
||||
|
||||
TLS_SRP_SHA_RSA_WITH_3DES_EDE_CBC_SHA = 0x0051
|
||||
TLS_SRP_SHA_RSA_WITH_AES_128_CBC_SHA = 0x0054
|
||||
TLS_SRP_SHA_RSA_WITH_AES_256_CBC_SHA = 0x0057
|
||||
|
||||
TLS_RSA_WITH_3DES_EDE_CBC_SHA = 0x000A
|
||||
TLS_RSA_WITH_AES_128_CBC_SHA = 0x002F
|
||||
TLS_RSA_WITH_AES_256_CBC_SHA = 0x0035
|
||||
TLS_RSA_WITH_RC4_128_SHA = 0x0005
|
||||
|
||||
srpSuites = []
|
||||
srpSuites.append(TLS_SRP_SHA_WITH_3DES_EDE_CBC_SHA)
|
||||
srpSuites.append(TLS_SRP_SHA_WITH_AES_128_CBC_SHA)
|
||||
srpSuites.append(TLS_SRP_SHA_WITH_AES_256_CBC_SHA)
|
||||
def getSrpSuites(ciphers):
|
||||
suites = []
|
||||
for cipher in ciphers:
|
||||
if cipher == "aes128":
|
||||
suites.append(CipherSuite.TLS_SRP_SHA_WITH_AES_128_CBC_SHA)
|
||||
elif cipher == "aes256":
|
||||
suites.append(CipherSuite.TLS_SRP_SHA_WITH_AES_256_CBC_SHA)
|
||||
elif cipher == "3des":
|
||||
suites.append(CipherSuite.TLS_SRP_SHA_WITH_3DES_EDE_CBC_SHA)
|
||||
return suites
|
||||
getSrpSuites = staticmethod(getSrpSuites)
|
||||
|
||||
srpRsaSuites = []
|
||||
srpRsaSuites.append(TLS_SRP_SHA_RSA_WITH_3DES_EDE_CBC_SHA)
|
||||
srpRsaSuites.append(TLS_SRP_SHA_RSA_WITH_AES_128_CBC_SHA)
|
||||
srpRsaSuites.append(TLS_SRP_SHA_RSA_WITH_AES_256_CBC_SHA)
|
||||
def getSrpRsaSuites(ciphers):
|
||||
suites = []
|
||||
for cipher in ciphers:
|
||||
if cipher == "aes128":
|
||||
suites.append(CipherSuite.TLS_SRP_SHA_RSA_WITH_AES_128_CBC_SHA)
|
||||
elif cipher == "aes256":
|
||||
suites.append(CipherSuite.TLS_SRP_SHA_RSA_WITH_AES_256_CBC_SHA)
|
||||
elif cipher == "3des":
|
||||
suites.append(CipherSuite.TLS_SRP_SHA_RSA_WITH_3DES_EDE_CBC_SHA)
|
||||
return suites
|
||||
getSrpRsaSuites = staticmethod(getSrpRsaSuites)
|
||||
|
||||
rsaSuites = []
|
||||
rsaSuites.append(TLS_RSA_WITH_3DES_EDE_CBC_SHA)
|
||||
rsaSuites.append(TLS_RSA_WITH_AES_128_CBC_SHA)
|
||||
rsaSuites.append(TLS_RSA_WITH_AES_256_CBC_SHA)
|
||||
rsaSuites.append(TLS_RSA_WITH_RC4_128_SHA)
|
||||
def getRsaSuites(ciphers):
|
||||
suites = []
|
||||
for cipher in ciphers:
|
||||
if cipher == "aes128":
|
||||
suites.append(CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA)
|
||||
elif cipher == "aes256":
|
||||
suites.append(CipherSuite.TLS_RSA_WITH_AES_256_CBC_SHA)
|
||||
elif cipher == "rc4":
|
||||
suites.append(CipherSuite.TLS_RSA_WITH_RC4_128_SHA)
|
||||
elif cipher == "3des":
|
||||
suites.append(CipherSuite.TLS_RSA_WITH_3DES_EDE_CBC_SHA)
|
||||
return suites
|
||||
getRsaSuites = staticmethod(getRsaSuites)
|
||||
|
||||
tripleDESSuites = []
|
||||
tripleDESSuites.append(TLS_SRP_SHA_WITH_3DES_EDE_CBC_SHA)
|
||||
tripleDESSuites.append(TLS_SRP_SHA_RSA_WITH_3DES_EDE_CBC_SHA)
|
||||
tripleDESSuites.append(TLS_RSA_WITH_3DES_EDE_CBC_SHA)
|
||||
|
||||
aes128Suites = []
|
||||
aes128Suites.append(TLS_SRP_SHA_WITH_AES_128_CBC_SHA)
|
||||
aes128Suites.append(TLS_SRP_SHA_RSA_WITH_AES_128_CBC_SHA)
|
||||
aes128Suites.append(TLS_RSA_WITH_AES_128_CBC_SHA)
|
||||
|
||||
aes256Suites = []
|
||||
aes256Suites.append(TLS_SRP_SHA_WITH_AES_256_CBC_SHA)
|
||||
aes256Suites.append(TLS_SRP_SHA_RSA_WITH_AES_256_CBC_SHA)
|
||||
aes256Suites.append(TLS_RSA_WITH_AES_256_CBC_SHA)
|
||||
|
||||
rc4Suites = []
|
||||
rc4Suites.append(TLS_RSA_WITH_RC4_128_SHA)
|
||||
|
||||
|
||||
class Fault:
|
||||
badUsername = 101
|
||||
badPassword = 102
|
||||
badA = 103
|
||||
clientSrpFaults = list(range(101,104))
|
||||
|
||||
badVerifyMessage = 601
|
||||
clientCertFaults = list(range(601,602))
|
||||
|
||||
badPremasterPadding = 501
|
||||
shortPremasterSecret = 502
|
||||
clientNoAuthFaults = list(range(501,503))
|
||||
|
||||
badIdentifier = 401
|
||||
badSharedKey = 402
|
||||
clientSharedKeyFaults = list(range(401,403))
|
||||
|
||||
badB = 201
|
||||
serverFaults = list(range(201,202))
|
||||
|
||||
badFinished = 300
|
||||
badMAC = 301
|
||||
badPadding = 302
|
||||
genericFaults = list(range(300,303))
|
||||
|
||||
faultAlerts = {\
|
||||
badUsername: (AlertDescription.unknown_srp_username, \
|
||||
AlertDescription.bad_record_mac),\
|
||||
badPassword: (AlertDescription.bad_record_mac,),\
|
||||
badA: (AlertDescription.illegal_parameter,),\
|
||||
badIdentifier: (AlertDescription.handshake_failure,),\
|
||||
badSharedKey: (AlertDescription.bad_record_mac,),\
|
||||
badPremasterPadding: (AlertDescription.bad_record_mac,),\
|
||||
shortPremasterSecret: (AlertDescription.bad_record_mac,),\
|
||||
badVerifyMessage: (AlertDescription.decrypt_error,),\
|
||||
badFinished: (AlertDescription.decrypt_error,),\
|
||||
badMAC: (AlertDescription.bad_record_mac,),\
|
||||
badPadding: (AlertDescription.bad_record_mac,)
|
||||
}
|
||||
|
||||
faultNames = {\
|
||||
badUsername: "bad username",\
|
||||
badPassword: "bad password",\
|
||||
badA: "bad A",\
|
||||
badIdentifier: "bad identifier",\
|
||||
badSharedKey: "bad sharedkey",\
|
||||
badPremasterPadding: "bad premaster padding",\
|
||||
shortPremasterSecret: "short premaster secret",\
|
||||
badVerifyMessage: "bad verify message",\
|
||||
badFinished: "bad finished message",\
|
||||
badMAC: "bad MAC",\
|
||||
badPadding: "bad padding"
|
||||
}
|
||||
@@ -1,149 +0,0 @@
|
||||
"""Exception classes.
|
||||
@sort: TLSError, TLSAbruptCloseError, TLSAlert, TLSLocalAlert, TLSRemoteAlert,
|
||||
TLSAuthenticationError, TLSNoAuthenticationError, TLSAuthenticationTypeError,
|
||||
TLSFingerprintError, TLSAuthorizationError, TLSValidationError, TLSFaultError
|
||||
"""
|
||||
|
||||
from .constants import AlertDescription, AlertLevel
|
||||
|
||||
class TLSError(Exception):
|
||||
"""Base class for all TLS Lite exceptions."""
|
||||
pass
|
||||
|
||||
class TLSAbruptCloseError(TLSError):
|
||||
"""The socket was closed without a proper TLS shutdown.
|
||||
|
||||
The TLS specification mandates that an alert of some sort
|
||||
must be sent before the underlying socket is closed. If the socket
|
||||
is closed without this, it could signify that an attacker is trying
|
||||
to truncate the connection. It could also signify a misbehaving
|
||||
TLS implementation, or a random network failure.
|
||||
"""
|
||||
pass
|
||||
|
||||
class TLSAlert(TLSError):
|
||||
"""A TLS alert has been signalled."""
|
||||
pass
|
||||
|
||||
_descriptionStr = {\
|
||||
AlertDescription.close_notify: "close_notify",\
|
||||
AlertDescription.unexpected_message: "unexpected_message",\
|
||||
AlertDescription.bad_record_mac: "bad_record_mac",\
|
||||
AlertDescription.decryption_failed: "decryption_failed",\
|
||||
AlertDescription.record_overflow: "record_overflow",\
|
||||
AlertDescription.decompression_failure: "decompression_failure",\
|
||||
AlertDescription.handshake_failure: "handshake_failure",\
|
||||
AlertDescription.no_certificate: "no certificate",\
|
||||
AlertDescription.bad_certificate: "bad_certificate",\
|
||||
AlertDescription.unsupported_certificate: "unsupported_certificate",\
|
||||
AlertDescription.certificate_revoked: "certificate_revoked",\
|
||||
AlertDescription.certificate_expired: "certificate_expired",\
|
||||
AlertDescription.certificate_unknown: "certificate_unknown",\
|
||||
AlertDescription.illegal_parameter: "illegal_parameter",\
|
||||
AlertDescription.unknown_ca: "unknown_ca",\
|
||||
AlertDescription.access_denied: "access_denied",\
|
||||
AlertDescription.decode_error: "decode_error",\
|
||||
AlertDescription.decrypt_error: "decrypt_error",\
|
||||
AlertDescription.export_restriction: "export_restriction",\
|
||||
AlertDescription.protocol_version: "protocol_version",\
|
||||
AlertDescription.insufficient_security: "insufficient_security",\
|
||||
AlertDescription.internal_error: "internal_error",\
|
||||
AlertDescription.user_canceled: "user_canceled",\
|
||||
AlertDescription.no_renegotiation: "no_renegotiation",\
|
||||
AlertDescription.unknown_srp_username: "unknown_srp_username",\
|
||||
AlertDescription.missing_srp_username: "missing_srp_username"}
|
||||
|
||||
class TLSLocalAlert(TLSAlert):
|
||||
"""A TLS alert has been signalled by the local implementation.
|
||||
|
||||
@type description: int
|
||||
@ivar description: Set to one of the constants in
|
||||
L{tlslite.constants.AlertDescription}
|
||||
|
||||
@type level: int
|
||||
@ivar level: Set to one of the constants in
|
||||
L{tlslite.constants.AlertLevel}
|
||||
|
||||
@type message: str
|
||||
@ivar message: Description of what went wrong.
|
||||
"""
|
||||
def __init__(self, alert, message=None):
|
||||
self.description = alert.description
|
||||
self.level = alert.level
|
||||
self.message = message
|
||||
|
||||
def __str__(self):
|
||||
alertStr = TLSAlert._descriptionStr.get(self.description)
|
||||
if alertStr == None:
|
||||
alertStr = str(self.description)
|
||||
if self.message:
|
||||
return alertStr + ": " + self.message
|
||||
else:
|
||||
return alertStr
|
||||
|
||||
class TLSRemoteAlert(TLSAlert):
|
||||
"""A TLS alert has been signalled by the remote implementation.
|
||||
|
||||
@type description: int
|
||||
@ivar description: Set to one of the constants in
|
||||
L{tlslite.constants.AlertDescription}
|
||||
|
||||
@type level: int
|
||||
@ivar level: Set to one of the constants in
|
||||
L{tlslite.constants.AlertLevel}
|
||||
"""
|
||||
def __init__(self, alert):
|
||||
self.description = alert.description
|
||||
self.level = alert.level
|
||||
|
||||
def __str__(self):
|
||||
alertStr = TLSAlert._descriptionStr.get(self.description)
|
||||
if alertStr == None:
|
||||
alertStr = str(self.description)
|
||||
return alertStr
|
||||
|
||||
class TLSAuthenticationError(TLSError):
|
||||
"""The handshake succeeded, but the other party's authentication
|
||||
was inadequate.
|
||||
|
||||
This exception will only be raised when a
|
||||
L{tlslite.Checker.Checker} has been passed to a handshake function.
|
||||
The Checker will be invoked once the handshake completes, and if
|
||||
the Checker objects to how the other party authenticated, a
|
||||
subclass of this exception will be raised.
|
||||
"""
|
||||
pass
|
||||
|
||||
class TLSNoAuthenticationError(TLSAuthenticationError):
|
||||
"""The Checker was expecting the other party to authenticate with a
|
||||
certificate chain, but this did not occur."""
|
||||
pass
|
||||
|
||||
class TLSAuthenticationTypeError(TLSAuthenticationError):
|
||||
"""The Checker was expecting the other party to authenticate with a
|
||||
different type of certificate chain."""
|
||||
pass
|
||||
|
||||
class TLSFingerprintError(TLSAuthenticationError):
|
||||
"""The Checker was expecting the other party to authenticate with a
|
||||
certificate chain that matches a different fingerprint."""
|
||||
pass
|
||||
|
||||
class TLSAuthorizationError(TLSAuthenticationError):
|
||||
"""The Checker was expecting the other party to authenticate with a
|
||||
certificate chain that has a different authorization."""
|
||||
pass
|
||||
|
||||
class TLSValidationError(TLSAuthenticationError):
|
||||
"""The Checker has determined that the other party's certificate
|
||||
chain is invalid."""
|
||||
pass
|
||||
|
||||
class TLSFaultError(TLSError):
|
||||
"""The other party responded incorrectly to an induced fault.
|
||||
|
||||
This exception will only occur during fault testing, when a
|
||||
TLSConnection's fault variable is set to induce some sort of
|
||||
faulty behavior, and the other party doesn't respond appropriately.
|
||||
"""
|
||||
pass
|
||||
@@ -1,235 +0,0 @@
|
||||
"""
|
||||
A state machine for using TLS Lite with asynchronous I/O.
|
||||
"""
|
||||
|
||||
class AsyncStateMachine:
|
||||
"""
|
||||
This is an abstract class that's used to integrate TLS Lite with
|
||||
asyncore and Twisted.
|
||||
|
||||
This class signals wantsReadsEvent() and wantsWriteEvent(). When
|
||||
the underlying socket has become readable or writeable, the event
|
||||
should be passed to this class by calling inReadEvent() or
|
||||
inWriteEvent(). This class will then try to read or write through
|
||||
the socket, and will update its state appropriately.
|
||||
|
||||
This class will forward higher-level events to its subclass. For
|
||||
example, when a complete TLS record has been received,
|
||||
outReadEvent() will be called with the decrypted data.
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
self._clear()
|
||||
|
||||
def _clear(self):
|
||||
#These store the various asynchronous operations (i.e.
|
||||
#generators). Only one of them, at most, is ever active at a
|
||||
#time.
|
||||
self.handshaker = None
|
||||
self.closer = None
|
||||
self.reader = None
|
||||
self.writer = None
|
||||
|
||||
#This stores the result from the last call to the
|
||||
#currently active operation. If 0 it indicates that the
|
||||
#operation wants to read, if 1 it indicates that the
|
||||
#operation wants to write. If None, there is no active
|
||||
#operation.
|
||||
self.result = None
|
||||
|
||||
def _checkAssert(self, maxActive=1):
|
||||
#This checks that only one operation, at most, is
|
||||
#active, and that self.result is set appropriately.
|
||||
activeOps = 0
|
||||
if self.handshaker:
|
||||
activeOps += 1
|
||||
if self.closer:
|
||||
activeOps += 1
|
||||
if self.reader:
|
||||
activeOps += 1
|
||||
if self.writer:
|
||||
activeOps += 1
|
||||
|
||||
if self.result == None:
|
||||
if activeOps != 0:
|
||||
raise AssertionError()
|
||||
elif self.result in (0,1):
|
||||
if activeOps != 1:
|
||||
raise AssertionError()
|
||||
else:
|
||||
raise AssertionError()
|
||||
if activeOps > maxActive:
|
||||
raise AssertionError()
|
||||
|
||||
def wantsReadEvent(self):
|
||||
"""If the state machine wants to read.
|
||||
|
||||
If an operation is active, this returns whether or not the
|
||||
operation wants to read from the socket. If an operation is
|
||||
not active, this returns None.
|
||||
|
||||
@rtype: bool or None
|
||||
@return: If the state machine wants to read.
|
||||
"""
|
||||
if self.result != None:
|
||||
return self.result == 0
|
||||
return None
|
||||
|
||||
def wantsWriteEvent(self):
|
||||
"""If the state machine wants to write.
|
||||
|
||||
If an operation is active, this returns whether or not the
|
||||
operation wants to write to the socket. If an operation is
|
||||
not active, this returns None.
|
||||
|
||||
@rtype: bool or None
|
||||
@return: If the state machine wants to write.
|
||||
"""
|
||||
if self.result != None:
|
||||
return self.result == 1
|
||||
return None
|
||||
|
||||
def outConnectEvent(self):
|
||||
"""Called when a handshake operation completes.
|
||||
|
||||
May be overridden in subclass.
|
||||
"""
|
||||
pass
|
||||
|
||||
def outCloseEvent(self):
|
||||
"""Called when a close operation completes.
|
||||
|
||||
May be overridden in subclass.
|
||||
"""
|
||||
pass
|
||||
|
||||
def outReadEvent(self, readBuffer):
|
||||
"""Called when a read operation completes.
|
||||
|
||||
May be overridden in subclass."""
|
||||
pass
|
||||
|
||||
def outWriteEvent(self):
|
||||
"""Called when a write operation completes.
|
||||
|
||||
May be overridden in subclass."""
|
||||
pass
|
||||
|
||||
def inReadEvent(self):
|
||||
"""Tell the state machine it can read from the socket."""
|
||||
try:
|
||||
self._checkAssert()
|
||||
if self.handshaker:
|
||||
self._doHandshakeOp()
|
||||
elif self.closer:
|
||||
self._doCloseOp()
|
||||
elif self.reader:
|
||||
self._doReadOp()
|
||||
elif self.writer:
|
||||
self._doWriteOp()
|
||||
else:
|
||||
self.reader = self.tlsConnection.readAsync(16384)
|
||||
self._doReadOp()
|
||||
except:
|
||||
self._clear()
|
||||
raise
|
||||
|
||||
def inWriteEvent(self):
|
||||
"""Tell the state machine it can write to the socket."""
|
||||
try:
|
||||
self._checkAssert()
|
||||
if self.handshaker:
|
||||
self._doHandshakeOp()
|
||||
elif self.closer:
|
||||
self._doCloseOp()
|
||||
elif self.reader:
|
||||
self._doReadOp()
|
||||
elif self.writer:
|
||||
self._doWriteOp()
|
||||
else:
|
||||
self.outWriteEvent()
|
||||
except:
|
||||
self._clear()
|
||||
raise
|
||||
|
||||
def _doHandshakeOp(self):
|
||||
try:
|
||||
self.result = self.handshaker.next()
|
||||
except StopIteration:
|
||||
self.handshaker = None
|
||||
self.result = None
|
||||
self.outConnectEvent()
|
||||
|
||||
def _doCloseOp(self):
|
||||
try:
|
||||
self.result = self.closer.next()
|
||||
except StopIteration:
|
||||
self.closer = None
|
||||
self.result = None
|
||||
self.outCloseEvent()
|
||||
|
||||
def _doReadOp(self):
|
||||
self.result = self.reader.next()
|
||||
if not self.result in (0,1):
|
||||
readBuffer = self.result
|
||||
self.reader = None
|
||||
self.result = None
|
||||
self.outReadEvent(readBuffer)
|
||||
|
||||
def _doWriteOp(self):
|
||||
try:
|
||||
self.result = self.writer.next()
|
||||
except StopIteration:
|
||||
self.writer = None
|
||||
self.result = None
|
||||
|
||||
def setHandshakeOp(self, handshaker):
|
||||
"""Start a handshake operation.
|
||||
|
||||
@type handshaker: generator
|
||||
@param handshaker: A generator created by using one of the
|
||||
asynchronous handshake functions (i.e. handshakeServerAsync, or
|
||||
handshakeClientxxx(..., async=True).
|
||||
"""
|
||||
try:
|
||||
self._checkAssert(0)
|
||||
self.handshaker = handshaker
|
||||
self._doHandshakeOp()
|
||||
except:
|
||||
self._clear()
|
||||
raise
|
||||
|
||||
def setServerHandshakeOp(self, **args):
|
||||
"""Start a handshake operation.
|
||||
|
||||
The arguments passed to this function will be forwarded to
|
||||
L{tlslite.TLSConnection.TLSConnection.handshakeServerAsync}.
|
||||
"""
|
||||
handshaker = self.tlsConnection.handshakeServerAsync(**args)
|
||||
self.setHandshakeOp(handshaker)
|
||||
|
||||
def setCloseOp(self):
|
||||
"""Start a close operation.
|
||||
"""
|
||||
try:
|
||||
self._checkAssert(0)
|
||||
self.closer = self.tlsConnection.closeAsync()
|
||||
self._doCloseOp()
|
||||
except:
|
||||
self._clear()
|
||||
raise
|
||||
|
||||
def setWriteOp(self, writeBuffer):
|
||||
"""Start a write operation.
|
||||
|
||||
@type writeBuffer: str
|
||||
@param writeBuffer: The string to transmit.
|
||||
"""
|
||||
try:
|
||||
self._checkAssert(0)
|
||||
self.writer = self.tlsConnection.writeAsync(writeBuffer)
|
||||
self._doWriteOp()
|
||||
except:
|
||||
self._clear()
|
||||
raise
|
||||
|
||||
@@ -1,163 +0,0 @@
|
||||
"""
|
||||
A helper class for using TLS Lite with stdlib clients
|
||||
(httplib, xmlrpclib, imaplib, poplib).
|
||||
"""
|
||||
|
||||
from gdata.tlslite.Checker import Checker
|
||||
|
||||
class ClientHelper:
|
||||
"""This is a helper class used to integrate TLS Lite with various
|
||||
TLS clients (e.g. poplib, smtplib, httplib, etc.)"""
|
||||
|
||||
def __init__(self,
|
||||
username=None, password=None, sharedKey=None,
|
||||
certChain=None, privateKey=None,
|
||||
cryptoID=None, protocol=None,
|
||||
x509Fingerprint=None,
|
||||
x509TrustList=None, x509CommonName=None,
|
||||
settings = None):
|
||||
"""
|
||||
For client authentication, use one of these argument
|
||||
combinations:
|
||||
- username, password (SRP)
|
||||
- username, sharedKey (shared-key)
|
||||
- certChain, privateKey (certificate)
|
||||
|
||||
For server authentication, you can either rely on the
|
||||
implicit mutual authentication performed by SRP or
|
||||
shared-keys, or you can do certificate-based server
|
||||
authentication with one of these argument combinations:
|
||||
- cryptoID[, protocol] (requires cryptoIDlib)
|
||||
- x509Fingerprint
|
||||
- x509TrustList[, x509CommonName] (requires cryptlib_py)
|
||||
|
||||
Certificate-based server authentication is compatible with
|
||||
SRP or certificate-based client authentication. It is
|
||||
not compatible with shared-keys.
|
||||
|
||||
The constructor does not perform the TLS handshake itself, but
|
||||
simply stores these arguments for later. The handshake is
|
||||
performed only when this class needs to connect with the
|
||||
server. Then you should be prepared to handle TLS-specific
|
||||
exceptions. See the client handshake functions in
|
||||
L{tlslite.TLSConnection.TLSConnection} for details on which
|
||||
exceptions might be raised.
|
||||
|
||||
@type username: str
|
||||
@param username: SRP or shared-key username. Requires the
|
||||
'password' or 'sharedKey' argument.
|
||||
|
||||
@type password: str
|
||||
@param password: SRP password for mutual authentication.
|
||||
Requires the 'username' argument.
|
||||
|
||||
@type sharedKey: str
|
||||
@param sharedKey: Shared key for mutual authentication.
|
||||
Requires the 'username' argument.
|
||||
|
||||
@type certChain: L{tlslite.X509CertChain.X509CertChain} or
|
||||
L{cryptoIDlib.CertChain.CertChain}
|
||||
@param certChain: Certificate chain for client authentication.
|
||||
Requires the 'privateKey' argument. Excludes the SRP or
|
||||
shared-key related arguments.
|
||||
|
||||
@type privateKey: L{tlslite.utils.RSAKey.RSAKey}
|
||||
@param privateKey: Private key for client authentication.
|
||||
Requires the 'certChain' argument. Excludes the SRP or
|
||||
shared-key related arguments.
|
||||
|
||||
@type cryptoID: str
|
||||
@param cryptoID: cryptoID for server authentication. Mutually
|
||||
exclusive with the 'x509...' arguments.
|
||||
|
||||
@type protocol: str
|
||||
@param protocol: cryptoID protocol URI for server
|
||||
authentication. Requires the 'cryptoID' argument.
|
||||
|
||||
@type x509Fingerprint: str
|
||||
@param x509Fingerprint: Hex-encoded X.509 fingerprint for
|
||||
server authentication. Mutually exclusive with the 'cryptoID'
|
||||
and 'x509TrustList' arguments.
|
||||
|
||||
@type x509TrustList: list of L{tlslite.X509.X509}
|
||||
@param x509TrustList: A list of trusted root certificates. The
|
||||
other party must present a certificate chain which extends to
|
||||
one of these root certificates. The cryptlib_py module must be
|
||||
installed to use this parameter. Mutually exclusive with the
|
||||
'cryptoID' and 'x509Fingerprint' arguments.
|
||||
|
||||
@type x509CommonName: str
|
||||
@param x509CommonName: The end-entity certificate's 'CN' field
|
||||
must match this value. For a web server, this is typically a
|
||||
server name such as 'www.amazon.com'. Mutually exclusive with
|
||||
the 'cryptoID' and 'x509Fingerprint' arguments. Requires the
|
||||
'x509TrustList' argument.
|
||||
|
||||
@type settings: L{tlslite.HandshakeSettings.HandshakeSettings}
|
||||
@param settings: Various settings which can be used to control
|
||||
the ciphersuites, certificate types, and SSL/TLS versions
|
||||
offered by the client.
|
||||
"""
|
||||
|
||||
self.username = None
|
||||
self.password = None
|
||||
self.sharedKey = None
|
||||
self.certChain = None
|
||||
self.privateKey = None
|
||||
self.checker = None
|
||||
|
||||
#SRP Authentication
|
||||
if username and password and not \
|
||||
(sharedKey or certChain or privateKey):
|
||||
self.username = username
|
||||
self.password = password
|
||||
|
||||
#Shared Key Authentication
|
||||
elif username and sharedKey and not \
|
||||
(password or certChain or privateKey):
|
||||
self.username = username
|
||||
self.sharedKey = sharedKey
|
||||
|
||||
#Certificate Chain Authentication
|
||||
elif certChain and privateKey and not \
|
||||
(username or password or sharedKey):
|
||||
self.certChain = certChain
|
||||
self.privateKey = privateKey
|
||||
|
||||
#No Authentication
|
||||
elif not password and not username and not \
|
||||
sharedKey and not certChain and not privateKey:
|
||||
pass
|
||||
|
||||
else:
|
||||
raise ValueError("Bad parameters")
|
||||
|
||||
#Authenticate the server based on its cryptoID or fingerprint
|
||||
if sharedKey and (cryptoID or protocol or x509Fingerprint):
|
||||
raise ValueError("Can't use shared keys with other forms of"\
|
||||
"authentication")
|
||||
|
||||
self.checker = Checker(cryptoID, protocol, x509Fingerprint,
|
||||
x509TrustList, x509CommonName)
|
||||
self.settings = settings
|
||||
|
||||
self.tlsSession = None
|
||||
|
||||
def _handshake(self, tlsConnection):
|
||||
if self.username and self.password:
|
||||
tlsConnection.handshakeClientSRP(username=self.username,
|
||||
password=self.password,
|
||||
checker=self.checker,
|
||||
settings=self.settings,
|
||||
session=self.tlsSession)
|
||||
elif self.username and self.sharedKey:
|
||||
tlsConnection.handshakeClientSharedKey(username=self.username,
|
||||
sharedKey=self.sharedKey,
|
||||
settings=self.settings)
|
||||
else:
|
||||
tlsConnection.handshakeClientCert(certChain=self.certChain,
|
||||
privateKey=self.privateKey,
|
||||
checker=self.checker,
|
||||
settings=self.settings,
|
||||
session=self.tlsSession)
|
||||
self.tlsSession = tlsConnection.session
|
||||
@@ -1,169 +0,0 @@
|
||||
"""TLS Lite + httplib."""
|
||||
|
||||
import socket
|
||||
import httplib
|
||||
from gdata.tlslite.TLSConnection import TLSConnection
|
||||
from gdata.tlslite.integration.ClientHelper import ClientHelper
|
||||
|
||||
|
||||
class HTTPBaseTLSConnection(httplib.HTTPConnection):
|
||||
"""This abstract class provides a framework for adding TLS support
|
||||
to httplib."""
|
||||
|
||||
default_port = 443
|
||||
|
||||
def __init__(self, host, port=None, strict=None):
|
||||
if strict == None:
|
||||
#Python 2.2 doesn't support strict
|
||||
httplib.HTTPConnection.__init__(self, host, port)
|
||||
else:
|
||||
httplib.HTTPConnection.__init__(self, host, port, strict)
|
||||
|
||||
def connect(self):
|
||||
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||
if hasattr(sock, 'settimeout'):
|
||||
sock.settimeout(10)
|
||||
sock.connect((self.host, self.port))
|
||||
|
||||
#Use a TLSConnection to emulate a socket
|
||||
self.sock = TLSConnection(sock)
|
||||
|
||||
#When httplib closes this, close the socket
|
||||
self.sock.closeSocket = True
|
||||
self._handshake(self.sock)
|
||||
|
||||
def _handshake(self, tlsConnection):
|
||||
"""Called to perform some sort of handshake.
|
||||
|
||||
This method must be overridden in a subclass to do some type of
|
||||
handshake. This method will be called after the socket has
|
||||
been connected but before any data has been sent. If this
|
||||
method does not raise an exception, the TLS connection will be
|
||||
considered valid.
|
||||
|
||||
This method may (or may not) be called every time an HTTP
|
||||
request is performed, depending on whether the underlying HTTP
|
||||
connection is persistent.
|
||||
|
||||
@type tlsConnection: L{tlslite.TLSConnection.TLSConnection}
|
||||
@param tlsConnection: The connection to perform the handshake
|
||||
on.
|
||||
"""
|
||||
raise NotImplementedError()
|
||||
|
||||
|
||||
class HTTPTLSConnection(HTTPBaseTLSConnection, ClientHelper):
|
||||
"""This class extends L{HTTPBaseTLSConnection} to support the
|
||||
common types of handshaking."""
|
||||
|
||||
def __init__(self, host, port=None,
|
||||
username=None, password=None, sharedKey=None,
|
||||
certChain=None, privateKey=None,
|
||||
cryptoID=None, protocol=None,
|
||||
x509Fingerprint=None,
|
||||
x509TrustList=None, x509CommonName=None,
|
||||
settings = None):
|
||||
"""Create a new HTTPTLSConnection.
|
||||
|
||||
For client authentication, use one of these argument
|
||||
combinations:
|
||||
- username, password (SRP)
|
||||
- username, sharedKey (shared-key)
|
||||
- certChain, privateKey (certificate)
|
||||
|
||||
For server authentication, you can either rely on the
|
||||
implicit mutual authentication performed by SRP or
|
||||
shared-keys, or you can do certificate-based server
|
||||
authentication with one of these argument combinations:
|
||||
- cryptoID[, protocol] (requires cryptoIDlib)
|
||||
- x509Fingerprint
|
||||
- x509TrustList[, x509CommonName] (requires cryptlib_py)
|
||||
|
||||
Certificate-based server authentication is compatible with
|
||||
SRP or certificate-based client authentication. It is
|
||||
not compatible with shared-keys.
|
||||
|
||||
The constructor does not perform the TLS handshake itself, but
|
||||
simply stores these arguments for later. The handshake is
|
||||
performed only when this class needs to connect with the
|
||||
server. Thus you should be prepared to handle TLS-specific
|
||||
exceptions when calling methods inherited from
|
||||
L{httplib.HTTPConnection} such as request(), connect(), and
|
||||
send(). See the client handshake functions in
|
||||
L{tlslite.TLSConnection.TLSConnection} for details on which
|
||||
exceptions might be raised.
|
||||
|
||||
@type host: str
|
||||
@param host: Server to connect to.
|
||||
|
||||
@type port: int
|
||||
@param port: Port to connect to.
|
||||
|
||||
@type username: str
|
||||
@param username: SRP or shared-key username. Requires the
|
||||
'password' or 'sharedKey' argument.
|
||||
|
||||
@type password: str
|
||||
@param password: SRP password for mutual authentication.
|
||||
Requires the 'username' argument.
|
||||
|
||||
@type sharedKey: str
|
||||
@param sharedKey: Shared key for mutual authentication.
|
||||
Requires the 'username' argument.
|
||||
|
||||
@type certChain: L{tlslite.X509CertChain.X509CertChain} or
|
||||
L{cryptoIDlib.CertChain.CertChain}
|
||||
@param certChain: Certificate chain for client authentication.
|
||||
Requires the 'privateKey' argument. Excludes the SRP or
|
||||
shared-key related arguments.
|
||||
|
||||
@type privateKey: L{tlslite.utils.RSAKey.RSAKey}
|
||||
@param privateKey: Private key for client authentication.
|
||||
Requires the 'certChain' argument. Excludes the SRP or
|
||||
shared-key related arguments.
|
||||
|
||||
@type cryptoID: str
|
||||
@param cryptoID: cryptoID for server authentication. Mutually
|
||||
exclusive with the 'x509...' arguments.
|
||||
|
||||
@type protocol: str
|
||||
@param protocol: cryptoID protocol URI for server
|
||||
authentication. Requires the 'cryptoID' argument.
|
||||
|
||||
@type x509Fingerprint: str
|
||||
@param x509Fingerprint: Hex-encoded X.509 fingerprint for
|
||||
server authentication. Mutually exclusive with the 'cryptoID'
|
||||
and 'x509TrustList' arguments.
|
||||
|
||||
@type x509TrustList: list of L{tlslite.X509.X509}
|
||||
@param x509TrustList: A list of trusted root certificates. The
|
||||
other party must present a certificate chain which extends to
|
||||
one of these root certificates. The cryptlib_py module must be
|
||||
installed to use this parameter. Mutually exclusive with the
|
||||
'cryptoID' and 'x509Fingerprint' arguments.
|
||||
|
||||
@type x509CommonName: str
|
||||
@param x509CommonName: The end-entity certificate's 'CN' field
|
||||
must match this value. For a web server, this is typically a
|
||||
server name such as 'www.amazon.com'. Mutually exclusive with
|
||||
the 'cryptoID' and 'x509Fingerprint' arguments. Requires the
|
||||
'x509TrustList' argument.
|
||||
|
||||
@type settings: L{tlslite.HandshakeSettings.HandshakeSettings}
|
||||
@param settings: Various settings which can be used to control
|
||||
the ciphersuites, certificate types, and SSL/TLS versions
|
||||
offered by the client.
|
||||
"""
|
||||
|
||||
HTTPBaseTLSConnection.__init__(self, host, port)
|
||||
|
||||
ClientHelper.__init__(self,
|
||||
username, password, sharedKey,
|
||||
certChain, privateKey,
|
||||
cryptoID, protocol,
|
||||
x509Fingerprint,
|
||||
x509TrustList, x509CommonName,
|
||||
settings)
|
||||
|
||||
def _handshake(self, tlsConnection):
|
||||
ClientHelper._handshake(self, tlsConnection)
|
||||
@@ -1,132 +0,0 @@
|
||||
"""TLS Lite + imaplib."""
|
||||
|
||||
import socket
|
||||
from imaplib import IMAP4
|
||||
from gdata.tlslite.TLSConnection import TLSConnection
|
||||
from gdata.tlslite.integration.ClientHelper import ClientHelper
|
||||
|
||||
# IMAP TLS PORT
|
||||
IMAP4_TLS_PORT = 993
|
||||
|
||||
class IMAP4_TLS(IMAP4, ClientHelper):
|
||||
"""This class extends L{imaplib.IMAP4} with TLS support."""
|
||||
|
||||
def __init__(self, host = '', port = IMAP4_TLS_PORT,
|
||||
username=None, password=None, sharedKey=None,
|
||||
certChain=None, privateKey=None,
|
||||
cryptoID=None, protocol=None,
|
||||
x509Fingerprint=None,
|
||||
x509TrustList=None, x509CommonName=None,
|
||||
settings=None):
|
||||
"""Create a new IMAP4_TLS.
|
||||
|
||||
For client authentication, use one of these argument
|
||||
combinations:
|
||||
- username, password (SRP)
|
||||
- username, sharedKey (shared-key)
|
||||
- certChain, privateKey (certificate)
|
||||
|
||||
For server authentication, you can either rely on the
|
||||
implicit mutual authentication performed by SRP or
|
||||
shared-keys, or you can do certificate-based server
|
||||
authentication with one of these argument combinations:
|
||||
- cryptoID[, protocol] (requires cryptoIDlib)
|
||||
- x509Fingerprint
|
||||
- x509TrustList[, x509CommonName] (requires cryptlib_py)
|
||||
|
||||
Certificate-based server authentication is compatible with
|
||||
SRP or certificate-based client authentication. It is
|
||||
not compatible with shared-keys.
|
||||
|
||||
The caller should be prepared to handle TLS-specific
|
||||
exceptions. See the client handshake functions in
|
||||
L{tlslite.TLSConnection.TLSConnection} for details on which
|
||||
exceptions might be raised.
|
||||
|
||||
@type host: str
|
||||
@param host: Server to connect to.
|
||||
|
||||
@type port: int
|
||||
@param port: Port to connect to.
|
||||
|
||||
@type username: str
|
||||
@param username: SRP or shared-key username. Requires the
|
||||
'password' or 'sharedKey' argument.
|
||||
|
||||
@type password: str
|
||||
@param password: SRP password for mutual authentication.
|
||||
Requires the 'username' argument.
|
||||
|
||||
@type sharedKey: str
|
||||
@param sharedKey: Shared key for mutual authentication.
|
||||
Requires the 'username' argument.
|
||||
|
||||
@type certChain: L{tlslite.X509CertChain.X509CertChain} or
|
||||
L{cryptoIDlib.CertChain.CertChain}
|
||||
@param certChain: Certificate chain for client authentication.
|
||||
Requires the 'privateKey' argument. Excludes the SRP or
|
||||
shared-key related arguments.
|
||||
|
||||
@type privateKey: L{tlslite.utils.RSAKey.RSAKey}
|
||||
@param privateKey: Private key for client authentication.
|
||||
Requires the 'certChain' argument. Excludes the SRP or
|
||||
shared-key related arguments.
|
||||
|
||||
@type cryptoID: str
|
||||
@param cryptoID: cryptoID for server authentication. Mutually
|
||||
exclusive with the 'x509...' arguments.
|
||||
|
||||
@type protocol: str
|
||||
@param protocol: cryptoID protocol URI for server
|
||||
authentication. Requires the 'cryptoID' argument.
|
||||
|
||||
@type x509Fingerprint: str
|
||||
@param x509Fingerprint: Hex-encoded X.509 fingerprint for
|
||||
server authentication. Mutually exclusive with the 'cryptoID'
|
||||
and 'x509TrustList' arguments.
|
||||
|
||||
@type x509TrustList: list of L{tlslite.X509.X509}
|
||||
@param x509TrustList: A list of trusted root certificates. The
|
||||
other party must present a certificate chain which extends to
|
||||
one of these root certificates. The cryptlib_py module must be
|
||||
installed to use this parameter. Mutually exclusive with the
|
||||
'cryptoID' and 'x509Fingerprint' arguments.
|
||||
|
||||
@type x509CommonName: str
|
||||
@param x509CommonName: The end-entity certificate's 'CN' field
|
||||
must match this value. For a web server, this is typically a
|
||||
server name such as 'www.amazon.com'. Mutually exclusive with
|
||||
the 'cryptoID' and 'x509Fingerprint' arguments. Requires the
|
||||
'x509TrustList' argument.
|
||||
|
||||
@type settings: L{tlslite.HandshakeSettings.HandshakeSettings}
|
||||
@param settings: Various settings which can be used to control
|
||||
the ciphersuites, certificate types, and SSL/TLS versions
|
||||
offered by the client.
|
||||
"""
|
||||
|
||||
ClientHelper.__init__(self,
|
||||
username, password, sharedKey,
|
||||
certChain, privateKey,
|
||||
cryptoID, protocol,
|
||||
x509Fingerprint,
|
||||
x509TrustList, x509CommonName,
|
||||
settings)
|
||||
|
||||
IMAP4.__init__(self, host, port)
|
||||
|
||||
|
||||
def open(self, host = '', port = IMAP4_TLS_PORT):
|
||||
"""Setup connection to remote server on "host:port".
|
||||
|
||||
This connection will be used by the routines:
|
||||
read, readline, send, shutdown.
|
||||
"""
|
||||
self.host = host
|
||||
self.port = port
|
||||
self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||
self.sock.connect((host, port))
|
||||
self.sock = TLSConnection(self.sock)
|
||||
self.sock.closeSocket = True
|
||||
ClientHelper._handshake(self, self.sock)
|
||||
self.file = self.sock.makefile('rb')
|
||||
@@ -1,52 +0,0 @@
|
||||
|
||||
class IntegrationHelper:
|
||||
|
||||
def __init__(self,
|
||||
username=None, password=None, sharedKey=None,
|
||||
certChain=None, privateKey=None,
|
||||
cryptoID=None, protocol=None,
|
||||
x509Fingerprint=None,
|
||||
x509TrustList=None, x509CommonName=None,
|
||||
settings = None):
|
||||
|
||||
self.username = None
|
||||
self.password = None
|
||||
self.sharedKey = None
|
||||
self.certChain = None
|
||||
self.privateKey = None
|
||||
self.checker = None
|
||||
|
||||
#SRP Authentication
|
||||
if username and password and not \
|
||||
(sharedKey or certChain or privateKey):
|
||||
self.username = username
|
||||
self.password = password
|
||||
|
||||
#Shared Key Authentication
|
||||
elif username and sharedKey and not \
|
||||
(password or certChain or privateKey):
|
||||
self.username = username
|
||||
self.sharedKey = sharedKey
|
||||
|
||||
#Certificate Chain Authentication
|
||||
elif certChain and privateKey and not \
|
||||
(username or password or sharedKey):
|
||||
self.certChain = certChain
|
||||
self.privateKey = privateKey
|
||||
|
||||
#No Authentication
|
||||
elif not password and not username and not \
|
||||
sharedKey and not certChain and not privateKey:
|
||||
pass
|
||||
|
||||
else:
|
||||
raise ValueError("Bad parameters")
|
||||
|
||||
#Authenticate the server based on its cryptoID or fingerprint
|
||||
if sharedKey and (cryptoID or protocol or x509Fingerprint):
|
||||
raise ValueError("Can't use shared keys with other forms of"\
|
||||
"authentication")
|
||||
|
||||
self.checker = Checker(cryptoID, protocol, x509Fingerprint,
|
||||
x509TrustList, x509CommonName)
|
||||
self.settings = settings
|
||||
@@ -1,142 +0,0 @@
|
||||
"""TLS Lite + poplib."""
|
||||
|
||||
import socket
|
||||
from poplib import POP3
|
||||
from gdata.tlslite.TLSConnection import TLSConnection
|
||||
from gdata.tlslite.integration.ClientHelper import ClientHelper
|
||||
|
||||
# POP TLS PORT
|
||||
POP3_TLS_PORT = 995
|
||||
|
||||
class POP3_TLS(POP3, ClientHelper):
|
||||
"""This class extends L{poplib.POP3} with TLS support."""
|
||||
|
||||
def __init__(self, host, port = POP3_TLS_PORT,
|
||||
username=None, password=None, sharedKey=None,
|
||||
certChain=None, privateKey=None,
|
||||
cryptoID=None, protocol=None,
|
||||
x509Fingerprint=None,
|
||||
x509TrustList=None, x509CommonName=None,
|
||||
settings=None):
|
||||
"""Create a new POP3_TLS.
|
||||
|
||||
For client authentication, use one of these argument
|
||||
combinations:
|
||||
- username, password (SRP)
|
||||
- username, sharedKey (shared-key)
|
||||
- certChain, privateKey (certificate)
|
||||
|
||||
For server authentication, you can either rely on the
|
||||
implicit mutual authentication performed by SRP or
|
||||
shared-keys, or you can do certificate-based server
|
||||
authentication with one of these argument combinations:
|
||||
- cryptoID[, protocol] (requires cryptoIDlib)
|
||||
- x509Fingerprint
|
||||
- x509TrustList[, x509CommonName] (requires cryptlib_py)
|
||||
|
||||
Certificate-based server authentication is compatible with
|
||||
SRP or certificate-based client authentication. It is
|
||||
not compatible with shared-keys.
|
||||
|
||||
The caller should be prepared to handle TLS-specific
|
||||
exceptions. See the client handshake functions in
|
||||
L{tlslite.TLSConnection.TLSConnection} for details on which
|
||||
exceptions might be raised.
|
||||
|
||||
@type host: str
|
||||
@param host: Server to connect to.
|
||||
|
||||
@type port: int
|
||||
@param port: Port to connect to.
|
||||
|
||||
@type username: str
|
||||
@param username: SRP or shared-key username. Requires the
|
||||
'password' or 'sharedKey' argument.
|
||||
|
||||
@type password: str
|
||||
@param password: SRP password for mutual authentication.
|
||||
Requires the 'username' argument.
|
||||
|
||||
@type sharedKey: str
|
||||
@param sharedKey: Shared key for mutual authentication.
|
||||
Requires the 'username' argument.
|
||||
|
||||
@type certChain: L{tlslite.X509CertChain.X509CertChain} or
|
||||
L{cryptoIDlib.CertChain.CertChain}
|
||||
@param certChain: Certificate chain for client authentication.
|
||||
Requires the 'privateKey' argument. Excludes the SRP or
|
||||
shared-key related arguments.
|
||||
|
||||
@type privateKey: L{tlslite.utils.RSAKey.RSAKey}
|
||||
@param privateKey: Private key for client authentication.
|
||||
Requires the 'certChain' argument. Excludes the SRP or
|
||||
shared-key related arguments.
|
||||
|
||||
@type cryptoID: str
|
||||
@param cryptoID: cryptoID for server authentication. Mutually
|
||||
exclusive with the 'x509...' arguments.
|
||||
|
||||
@type protocol: str
|
||||
@param protocol: cryptoID protocol URI for server
|
||||
authentication. Requires the 'cryptoID' argument.
|
||||
|
||||
@type x509Fingerprint: str
|
||||
@param x509Fingerprint: Hex-encoded X.509 fingerprint for
|
||||
server authentication. Mutually exclusive with the 'cryptoID'
|
||||
and 'x509TrustList' arguments.
|
||||
|
||||
@type x509TrustList: list of L{tlslite.X509.X509}
|
||||
@param x509TrustList: A list of trusted root certificates. The
|
||||
other party must present a certificate chain which extends to
|
||||
one of these root certificates. The cryptlib_py module must be
|
||||
installed to use this parameter. Mutually exclusive with the
|
||||
'cryptoID' and 'x509Fingerprint' arguments.
|
||||
|
||||
@type x509CommonName: str
|
||||
@param x509CommonName: The end-entity certificate's 'CN' field
|
||||
must match this value. For a web server, this is typically a
|
||||
server name such as 'www.amazon.com'. Mutually exclusive with
|
||||
the 'cryptoID' and 'x509Fingerprint' arguments. Requires the
|
||||
'x509TrustList' argument.
|
||||
|
||||
@type settings: L{tlslite.HandshakeSettings.HandshakeSettings}
|
||||
@param settings: Various settings which can be used to control
|
||||
the ciphersuites, certificate types, and SSL/TLS versions
|
||||
offered by the client.
|
||||
"""
|
||||
|
||||
self.host = host
|
||||
self.port = port
|
||||
msg = "getaddrinfo returns an empty list"
|
||||
self.sock = None
|
||||
for res in socket.getaddrinfo(self.host, self.port, 0, socket.SOCK_STREAM):
|
||||
af, socktype, proto, canonname, sa = res
|
||||
try:
|
||||
self.sock = socket.socket(af, socktype, proto)
|
||||
self.sock.connect(sa)
|
||||
except socket.error, msg:
|
||||
if self.sock:
|
||||
self.sock.close()
|
||||
self.sock = None
|
||||
continue
|
||||
break
|
||||
if not self.sock:
|
||||
raise socket.error, msg
|
||||
|
||||
### New code below (all else copied from poplib)
|
||||
ClientHelper.__init__(self,
|
||||
username, password, sharedKey,
|
||||
certChain, privateKey,
|
||||
cryptoID, protocol,
|
||||
x509Fingerprint,
|
||||
x509TrustList, x509CommonName,
|
||||
settings)
|
||||
|
||||
self.sock = TLSConnection(self.sock)
|
||||
self.sock.closeSocket = True
|
||||
ClientHelper._handshake(self, self.sock)
|
||||
###
|
||||
|
||||
self.file = self.sock.makefile('rb')
|
||||
self._debugging = 0
|
||||
self.welcome = self._getresp()
|
||||
@@ -1,114 +0,0 @@
|
||||
"""TLS Lite + smtplib."""
|
||||
|
||||
from smtplib import SMTP
|
||||
from gdata.tlslite.TLSConnection import TLSConnection
|
||||
from gdata.tlslite.integration.ClientHelper import ClientHelper
|
||||
|
||||
class SMTP_TLS(SMTP):
|
||||
"""This class extends L{smtplib.SMTP} with TLS support."""
|
||||
|
||||
def starttls(self,
|
||||
username=None, password=None, sharedKey=None,
|
||||
certChain=None, privateKey=None,
|
||||
cryptoID=None, protocol=None,
|
||||
x509Fingerprint=None,
|
||||
x509TrustList=None, x509CommonName=None,
|
||||
settings=None):
|
||||
"""Puts the connection to the SMTP server into TLS mode.
|
||||
|
||||
If the server supports TLS, this will encrypt the rest of the SMTP
|
||||
session.
|
||||
|
||||
For client authentication, use one of these argument
|
||||
combinations:
|
||||
- username, password (SRP)
|
||||
- username, sharedKey (shared-key)
|
||||
- certChain, privateKey (certificate)
|
||||
|
||||
For server authentication, you can either rely on the
|
||||
implicit mutual authentication performed by SRP or
|
||||
shared-keys, or you can do certificate-based server
|
||||
authentication with one of these argument combinations:
|
||||
- cryptoID[, protocol] (requires cryptoIDlib)
|
||||
- x509Fingerprint
|
||||
- x509TrustList[, x509CommonName] (requires cryptlib_py)
|
||||
|
||||
Certificate-based server authentication is compatible with
|
||||
SRP or certificate-based client authentication. It is
|
||||
not compatible with shared-keys.
|
||||
|
||||
The caller should be prepared to handle TLS-specific
|
||||
exceptions. See the client handshake functions in
|
||||
L{tlslite.TLSConnection.TLSConnection} for details on which
|
||||
exceptions might be raised.
|
||||
|
||||
@type username: str
|
||||
@param username: SRP or shared-key username. Requires the
|
||||
'password' or 'sharedKey' argument.
|
||||
|
||||
@type password: str
|
||||
@param password: SRP password for mutual authentication.
|
||||
Requires the 'username' argument.
|
||||
|
||||
@type sharedKey: str
|
||||
@param sharedKey: Shared key for mutual authentication.
|
||||
Requires the 'username' argument.
|
||||
|
||||
@type certChain: L{tlslite.X509CertChain.X509CertChain} or
|
||||
L{cryptoIDlib.CertChain.CertChain}
|
||||
@param certChain: Certificate chain for client authentication.
|
||||
Requires the 'privateKey' argument. Excludes the SRP or
|
||||
shared-key related arguments.
|
||||
|
||||
@type privateKey: L{tlslite.utils.RSAKey.RSAKey}
|
||||
@param privateKey: Private key for client authentication.
|
||||
Requires the 'certChain' argument. Excludes the SRP or
|
||||
shared-key related arguments.
|
||||
|
||||
@type cryptoID: str
|
||||
@param cryptoID: cryptoID for server authentication. Mutually
|
||||
exclusive with the 'x509...' arguments.
|
||||
|
||||
@type protocol: str
|
||||
@param protocol: cryptoID protocol URI for server
|
||||
authentication. Requires the 'cryptoID' argument.
|
||||
|
||||
@type x509Fingerprint: str
|
||||
@param x509Fingerprint: Hex-encoded X.509 fingerprint for
|
||||
server authentication. Mutually exclusive with the 'cryptoID'
|
||||
and 'x509TrustList' arguments.
|
||||
|
||||
@type x509TrustList: list of L{tlslite.X509.X509}
|
||||
@param x509TrustList: A list of trusted root certificates. The
|
||||
other party must present a certificate chain which extends to
|
||||
one of these root certificates. The cryptlib_py module must be
|
||||
installed to use this parameter. Mutually exclusive with the
|
||||
'cryptoID' and 'x509Fingerprint' arguments.
|
||||
|
||||
@type x509CommonName: str
|
||||
@param x509CommonName: The end-entity certificate's 'CN' field
|
||||
must match this value. For a web server, this is typically a
|
||||
server name such as 'www.amazon.com'. Mutually exclusive with
|
||||
the 'cryptoID' and 'x509Fingerprint' arguments. Requires the
|
||||
'x509TrustList' argument.
|
||||
|
||||
@type settings: L{tlslite.HandshakeSettings.HandshakeSettings}
|
||||
@param settings: Various settings which can be used to control
|
||||
the ciphersuites, certificate types, and SSL/TLS versions
|
||||
offered by the client.
|
||||
"""
|
||||
(resp, reply) = self.docmd("STARTTLS")
|
||||
if resp == 220:
|
||||
helper = ClientHelper(
|
||||
username, password, sharedKey,
|
||||
certChain, privateKey,
|
||||
cryptoID, protocol,
|
||||
x509Fingerprint,
|
||||
x509TrustList, x509CommonName,
|
||||
settings)
|
||||
conn = TLSConnection(self.sock)
|
||||
conn.closeSocket = True
|
||||
helper._handshake(conn)
|
||||
self.sock = conn
|
||||
self.file = conn.makefile('rb')
|
||||
return (resp, reply)
|
||||
@@ -1,139 +0,0 @@
|
||||
"""TLS Lite + asyncore."""
|
||||
|
||||
|
||||
import asyncore
|
||||
from gdata.tlslite.TLSConnection import TLSConnection
|
||||
from AsyncStateMachine import AsyncStateMachine
|
||||
|
||||
|
||||
class TLSAsyncDispatcherMixIn(AsyncStateMachine):
|
||||
"""This class can be "mixed in" with an
|
||||
L{asyncore.dispatcher} to add TLS support.
|
||||
|
||||
This class essentially sits between the dispatcher and the select
|
||||
loop, intercepting events and only calling the dispatcher when
|
||||
applicable.
|
||||
|
||||
In the case of handle_read(), a read operation will be activated,
|
||||
and when it completes, the bytes will be placed in a buffer where
|
||||
the dispatcher can retrieve them by calling recv(), and the
|
||||
dispatcher's handle_read() will be called.
|
||||
|
||||
In the case of handle_write(), the dispatcher's handle_write() will
|
||||
be called, and when it calls send(), a write operation will be
|
||||
activated.
|
||||
|
||||
To use this class, you must combine it with an asyncore.dispatcher,
|
||||
and pass in a handshake operation with setServerHandshakeOp().
|
||||
|
||||
Below is an example of using this class with medusa. This class is
|
||||
mixed in with http_channel to create http_tls_channel. Note:
|
||||
1. the mix-in is listed first in the inheritance list
|
||||
|
||||
2. the input buffer size must be at least 16K, otherwise the
|
||||
dispatcher might not read all the bytes from the TLS layer,
|
||||
leaving some bytes in limbo.
|
||||
|
||||
3. IE seems to have a problem receiving a whole HTTP response in a
|
||||
single TLS record, so HTML pages containing '\\r\\n\\r\\n' won't
|
||||
be displayed on IE.
|
||||
|
||||
Add the following text into 'start_medusa.py', in the 'HTTP Server'
|
||||
section::
|
||||
|
||||
from tlslite.api import *
|
||||
s = open("./serverX509Cert.pem").read()
|
||||
x509 = X509()
|
||||
x509.parse(s)
|
||||
certChain = X509CertChain([x509])
|
||||
|
||||
s = open("./serverX509Key.pem").read()
|
||||
privateKey = parsePEMKey(s, private=True)
|
||||
|
||||
class http_tls_channel(TLSAsyncDispatcherMixIn,
|
||||
http_server.http_channel):
|
||||
ac_in_buffer_size = 16384
|
||||
|
||||
def __init__ (self, server, conn, addr):
|
||||
http_server.http_channel.__init__(self, server, conn, addr)
|
||||
TLSAsyncDispatcherMixIn.__init__(self, conn)
|
||||
self.tlsConnection.ignoreAbruptClose = True
|
||||
self.setServerHandshakeOp(certChain=certChain,
|
||||
privateKey=privateKey)
|
||||
|
||||
hs.channel_class = http_tls_channel
|
||||
|
||||
If the TLS layer raises an exception, the exception will be caught
|
||||
in asyncore.dispatcher, which will call close() on this class. The
|
||||
TLS layer always closes the TLS connection before raising an
|
||||
exception, so the close operation will complete right away, causing
|
||||
asyncore.dispatcher.close() to be called, which closes the socket
|
||||
and removes this instance from the asyncore loop.
|
||||
|
||||
"""
|
||||
|
||||
|
||||
def __init__(self, sock=None):
|
||||
AsyncStateMachine.__init__(self)
|
||||
|
||||
if sock:
|
||||
self.tlsConnection = TLSConnection(sock)
|
||||
|
||||
#Calculate the sibling I'm being mixed in with.
|
||||
#This is necessary since we override functions
|
||||
#like readable(), handle_read(), etc., but we
|
||||
#also want to call the sibling's versions.
|
||||
for cl in self.__class__.__bases__:
|
||||
if cl != TLSAsyncDispatcherMixIn and cl != AsyncStateMachine:
|
||||
self.siblingClass = cl
|
||||
break
|
||||
else:
|
||||
raise AssertionError()
|
||||
|
||||
def readable(self):
|
||||
result = self.wantsReadEvent()
|
||||
if result != None:
|
||||
return result
|
||||
return self.siblingClass.readable(self)
|
||||
|
||||
def writable(self):
|
||||
result = self.wantsWriteEvent()
|
||||
if result != None:
|
||||
return result
|
||||
return self.siblingClass.writable(self)
|
||||
|
||||
def handle_read(self):
|
||||
self.inReadEvent()
|
||||
|
||||
def handle_write(self):
|
||||
self.inWriteEvent()
|
||||
|
||||
def outConnectEvent(self):
|
||||
self.siblingClass.handle_connect(self)
|
||||
|
||||
def outCloseEvent(self):
|
||||
asyncore.dispatcher.close(self)
|
||||
|
||||
def outReadEvent(self, readBuffer):
|
||||
self.readBuffer = readBuffer
|
||||
self.siblingClass.handle_read(self)
|
||||
|
||||
def outWriteEvent(self):
|
||||
self.siblingClass.handle_write(self)
|
||||
|
||||
def recv(self, bufferSize=16384):
|
||||
if bufferSize < 16384 or self.readBuffer == None:
|
||||
raise AssertionError()
|
||||
returnValue = self.readBuffer
|
||||
self.readBuffer = None
|
||||
return returnValue
|
||||
|
||||
def send(self, writeBuffer):
|
||||
self.setWriteOp(writeBuffer)
|
||||
return len(writeBuffer)
|
||||
|
||||
def close(self):
|
||||
if hasattr(self, "tlsConnection"):
|
||||
self.setCloseOp()
|
||||
else:
|
||||
asyncore.dispatcher.close(self)
|
||||
@@ -1,59 +0,0 @@
|
||||
"""TLS Lite + SocketServer."""
|
||||
|
||||
from gdata.tlslite.TLSConnection import TLSConnection
|
||||
|
||||
class TLSSocketServerMixIn:
|
||||
"""
|
||||
This class can be mixed in with any L{SocketServer.TCPServer} to
|
||||
add TLS support.
|
||||
|
||||
To use this class, define a new class that inherits from it and
|
||||
some L{SocketServer.TCPServer} (with the mix-in first). Then
|
||||
implement the handshake() method, doing some sort of server
|
||||
handshake on the connection argument. If the handshake method
|
||||
returns True, the RequestHandler will be triggered. Below is a
|
||||
complete example of a threaded HTTPS server::
|
||||
|
||||
from SocketServer import *
|
||||
from BaseHTTPServer import *
|
||||
from SimpleHTTPServer import *
|
||||
from tlslite.api import *
|
||||
|
||||
s = open("./serverX509Cert.pem").read()
|
||||
x509 = X509()
|
||||
x509.parse(s)
|
||||
certChain = X509CertChain([x509])
|
||||
|
||||
s = open("./serverX509Key.pem").read()
|
||||
privateKey = parsePEMKey(s, private=True)
|
||||
|
||||
sessionCache = SessionCache()
|
||||
|
||||
class MyHTTPServer(ThreadingMixIn, TLSSocketServerMixIn,
|
||||
HTTPServer):
|
||||
def handshake(self, tlsConnection):
|
||||
try:
|
||||
tlsConnection.handshakeServer(certChain=certChain,
|
||||
privateKey=privateKey,
|
||||
sessionCache=sessionCache)
|
||||
tlsConnection.ignoreAbruptClose = True
|
||||
return True
|
||||
except TLSError, error:
|
||||
print "Handshake failure:", str(error)
|
||||
return False
|
||||
|
||||
httpd = MyHTTPServer(('localhost', 443), SimpleHTTPRequestHandler)
|
||||
httpd.serve_forever()
|
||||
"""
|
||||
|
||||
|
||||
def finish_request(self, sock, client_address):
|
||||
tlsConnection = TLSConnection(sock)
|
||||
if self.handshake(tlsConnection) == True:
|
||||
self.RequestHandlerClass(tlsConnection, client_address, self)
|
||||
tlsConnection.close()
|
||||
|
||||
#Implement this method to do some form of handshaking. Return True
|
||||
#if the handshake finishes properly and the request is authorized.
|
||||
def handshake(self, tlsConnection):
|
||||
raise NotImplementedError()
|
||||
@@ -1,196 +0,0 @@
|
||||
"""TLS Lite + Twisted."""
|
||||
|
||||
from twisted.protocols.policies import ProtocolWrapper, WrappingFactory
|
||||
from twisted.python.failure import Failure
|
||||
|
||||
from AsyncStateMachine import AsyncStateMachine
|
||||
from gdata.tlslite.TLSConnection import TLSConnection
|
||||
from gdata.tlslite.errors import *
|
||||
|
||||
import socket
|
||||
import errno
|
||||
|
||||
|
||||
#The TLSConnection is created around a "fake socket" that
|
||||
#plugs it into the underlying Twisted transport
|
||||
class _FakeSocket:
|
||||
def __init__(self, wrapper):
|
||||
self.wrapper = wrapper
|
||||
self.data = ""
|
||||
|
||||
def send(self, data):
|
||||
ProtocolWrapper.write(self.wrapper, data)
|
||||
return len(data)
|
||||
|
||||
def recv(self, numBytes):
|
||||
if self.data == "":
|
||||
raise socket.error, (errno.EWOULDBLOCK, "")
|
||||
returnData = self.data[:numBytes]
|
||||
self.data = self.data[numBytes:]
|
||||
return returnData
|
||||
|
||||
class TLSTwistedProtocolWrapper(ProtocolWrapper, AsyncStateMachine):
|
||||
"""This class can wrap Twisted protocols to add TLS support.
|
||||
|
||||
Below is a complete example of using TLS Lite with a Twisted echo
|
||||
server.
|
||||
|
||||
There are two server implementations below. Echo is the original
|
||||
protocol, which is oblivious to TLS. Echo1 subclasses Echo and
|
||||
negotiates TLS when the client connects. Echo2 subclasses Echo and
|
||||
negotiates TLS when the client sends "STARTTLS"::
|
||||
|
||||
from twisted.internet.protocol import Protocol, Factory
|
||||
from twisted.internet import reactor
|
||||
from twisted.protocols.policies import WrappingFactory
|
||||
from twisted.protocols.basic import LineReceiver
|
||||
from twisted.python import log
|
||||
from twisted.python.failure import Failure
|
||||
import sys
|
||||
from tlslite.api import *
|
||||
|
||||
s = open("./serverX509Cert.pem").read()
|
||||
x509 = X509()
|
||||
x509.parse(s)
|
||||
certChain = X509CertChain([x509])
|
||||
|
||||
s = open("./serverX509Key.pem").read()
|
||||
privateKey = parsePEMKey(s, private=True)
|
||||
|
||||
verifierDB = VerifierDB("verifierDB")
|
||||
verifierDB.open()
|
||||
|
||||
class Echo(LineReceiver):
|
||||
def connectionMade(self):
|
||||
self.transport.write("Welcome to the echo server!\\r\\n")
|
||||
|
||||
def lineReceived(self, line):
|
||||
self.transport.write(line + "\\r\\n")
|
||||
|
||||
class Echo1(Echo):
|
||||
def connectionMade(self):
|
||||
if not self.transport.tlsStarted:
|
||||
self.transport.setServerHandshakeOp(certChain=certChain,
|
||||
privateKey=privateKey,
|
||||
verifierDB=verifierDB)
|
||||
else:
|
||||
Echo.connectionMade(self)
|
||||
|
||||
def connectionLost(self, reason):
|
||||
pass #Handle any TLS exceptions here
|
||||
|
||||
class Echo2(Echo):
|
||||
def lineReceived(self, data):
|
||||
if data == "STARTTLS":
|
||||
self.transport.setServerHandshakeOp(certChain=certChain,
|
||||
privateKey=privateKey,
|
||||
verifierDB=verifierDB)
|
||||
else:
|
||||
Echo.lineReceived(self, data)
|
||||
|
||||
def connectionLost(self, reason):
|
||||
pass #Handle any TLS exceptions here
|
||||
|
||||
factory = Factory()
|
||||
factory.protocol = Echo1
|
||||
#factory.protocol = Echo2
|
||||
|
||||
wrappingFactory = WrappingFactory(factory)
|
||||
wrappingFactory.protocol = TLSTwistedProtocolWrapper
|
||||
|
||||
log.startLogging(sys.stdout)
|
||||
reactor.listenTCP(1079, wrappingFactory)
|
||||
reactor.run()
|
||||
|
||||
This class works as follows:
|
||||
|
||||
Data comes in and is given to the AsyncStateMachine for handling.
|
||||
AsyncStateMachine will forward events to this class, and we'll
|
||||
pass them on to the ProtocolHandler, which will proxy them to the
|
||||
wrapped protocol. The wrapped protocol may then call back into
|
||||
this class, and these calls will be proxied into the
|
||||
AsyncStateMachine.
|
||||
|
||||
The call graph looks like this:
|
||||
- self.dataReceived
|
||||
- AsyncStateMachine.inReadEvent
|
||||
- self.out(Connect|Close|Read)Event
|
||||
- ProtocolWrapper.(connectionMade|loseConnection|dataReceived)
|
||||
- self.(loseConnection|write|writeSequence)
|
||||
- AsyncStateMachine.(setCloseOp|setWriteOp)
|
||||
"""
|
||||
|
||||
#WARNING: IF YOU COPY-AND-PASTE THE ABOVE CODE, BE SURE TO REMOVE
|
||||
#THE EXTRA ESCAPING AROUND "\\r\\n"
|
||||
|
||||
def __init__(self, factory, wrappedProtocol):
|
||||
ProtocolWrapper.__init__(self, factory, wrappedProtocol)
|
||||
AsyncStateMachine.__init__(self)
|
||||
self.fakeSocket = _FakeSocket(self)
|
||||
self.tlsConnection = TLSConnection(self.fakeSocket)
|
||||
self.tlsStarted = False
|
||||
self.connectionLostCalled = False
|
||||
|
||||
def connectionMade(self):
|
||||
try:
|
||||
ProtocolWrapper.connectionMade(self)
|
||||
except TLSError, e:
|
||||
self.connectionLost(Failure(e))
|
||||
ProtocolWrapper.loseConnection(self)
|
||||
|
||||
def dataReceived(self, data):
|
||||
try:
|
||||
if not self.tlsStarted:
|
||||
ProtocolWrapper.dataReceived(self, data)
|
||||
else:
|
||||
self.fakeSocket.data += data
|
||||
while self.fakeSocket.data:
|
||||
AsyncStateMachine.inReadEvent(self)
|
||||
except TLSError, e:
|
||||
self.connectionLost(Failure(e))
|
||||
ProtocolWrapper.loseConnection(self)
|
||||
|
||||
def connectionLost(self, reason):
|
||||
if not self.connectionLostCalled:
|
||||
ProtocolWrapper.connectionLost(self, reason)
|
||||
self.connectionLostCalled = True
|
||||
|
||||
|
||||
def outConnectEvent(self):
|
||||
ProtocolWrapper.connectionMade(self)
|
||||
|
||||
def outCloseEvent(self):
|
||||
ProtocolWrapper.loseConnection(self)
|
||||
|
||||
def outReadEvent(self, data):
|
||||
if data == "":
|
||||
ProtocolWrapper.loseConnection(self)
|
||||
else:
|
||||
ProtocolWrapper.dataReceived(self, data)
|
||||
|
||||
|
||||
def setServerHandshakeOp(self, **args):
|
||||
self.tlsStarted = True
|
||||
AsyncStateMachine.setServerHandshakeOp(self, **args)
|
||||
|
||||
def loseConnection(self):
|
||||
if not self.tlsStarted:
|
||||
ProtocolWrapper.loseConnection(self)
|
||||
else:
|
||||
AsyncStateMachine.setCloseOp(self)
|
||||
|
||||
def write(self, data):
|
||||
if not self.tlsStarted:
|
||||
ProtocolWrapper.write(self, data)
|
||||
else:
|
||||
#Because of the FakeSocket, write operations are guaranteed to
|
||||
#terminate immediately.
|
||||
AsyncStateMachine.setWriteOp(self, data)
|
||||
|
||||
def writeSequence(self, seq):
|
||||
if not self.tlsStarted:
|
||||
ProtocolWrapper.writeSequence(self, seq)
|
||||
else:
|
||||
#Because of the FakeSocket, write operations are guaranteed to
|
||||
#terminate immediately.
|
||||
AsyncStateMachine.setWriteOp(self, "".join(seq))
|
||||
@@ -1,137 +0,0 @@
|
||||
"""TLS Lite + xmlrpclib."""
|
||||
|
||||
import xmlrpclib
|
||||
import httplib
|
||||
from gdata.tlslite.integration.HTTPTLSConnection import HTTPTLSConnection
|
||||
from gdata.tlslite.integration.ClientHelper import ClientHelper
|
||||
|
||||
|
||||
class XMLRPCTransport(xmlrpclib.Transport, ClientHelper):
|
||||
"""Handles an HTTPS transaction to an XML-RPC server."""
|
||||
|
||||
def __init__(self,
|
||||
username=None, password=None, sharedKey=None,
|
||||
certChain=None, privateKey=None,
|
||||
cryptoID=None, protocol=None,
|
||||
x509Fingerprint=None,
|
||||
x509TrustList=None, x509CommonName=None,
|
||||
settings=None):
|
||||
"""Create a new XMLRPCTransport.
|
||||
|
||||
An instance of this class can be passed to L{xmlrpclib.ServerProxy}
|
||||
to use TLS with XML-RPC calls::
|
||||
|
||||
from tlslite.api import XMLRPCTransport
|
||||
from xmlrpclib import ServerProxy
|
||||
|
||||
transport = XMLRPCTransport(user="alice", password="abra123")
|
||||
server = ServerProxy("https://localhost", transport)
|
||||
|
||||
For client authentication, use one of these argument
|
||||
combinations:
|
||||
- username, password (SRP)
|
||||
- username, sharedKey (shared-key)
|
||||
- certChain, privateKey (certificate)
|
||||
|
||||
For server authentication, you can either rely on the
|
||||
implicit mutual authentication performed by SRP or
|
||||
shared-keys, or you can do certificate-based server
|
||||
authentication with one of these argument combinations:
|
||||
- cryptoID[, protocol] (requires cryptoIDlib)
|
||||
- x509Fingerprint
|
||||
- x509TrustList[, x509CommonName] (requires cryptlib_py)
|
||||
|
||||
Certificate-based server authentication is compatible with
|
||||
SRP or certificate-based client authentication. It is
|
||||
not compatible with shared-keys.
|
||||
|
||||
The constructor does not perform the TLS handshake itself, but
|
||||
simply stores these arguments for later. The handshake is
|
||||
performed only when this class needs to connect with the
|
||||
server. Thus you should be prepared to handle TLS-specific
|
||||
exceptions when calling methods of L{xmlrpclib.ServerProxy}. See the
|
||||
client handshake functions in
|
||||
L{tlslite.TLSConnection.TLSConnection} for details on which
|
||||
exceptions might be raised.
|
||||
|
||||
@type username: str
|
||||
@param username: SRP or shared-key username. Requires the
|
||||
'password' or 'sharedKey' argument.
|
||||
|
||||
@type password: str
|
||||
@param password: SRP password for mutual authentication.
|
||||
Requires the 'username' argument.
|
||||
|
||||
@type sharedKey: str
|
||||
@param sharedKey: Shared key for mutual authentication.
|
||||
Requires the 'username' argument.
|
||||
|
||||
@type certChain: L{tlslite.X509CertChain.X509CertChain} or
|
||||
L{cryptoIDlib.CertChain.CertChain}
|
||||
@param certChain: Certificate chain for client authentication.
|
||||
Requires the 'privateKey' argument. Excludes the SRP or
|
||||
shared-key related arguments.
|
||||
|
||||
@type privateKey: L{tlslite.utils.RSAKey.RSAKey}
|
||||
@param privateKey: Private key for client authentication.
|
||||
Requires the 'certChain' argument. Excludes the SRP or
|
||||
shared-key related arguments.
|
||||
|
||||
@type cryptoID: str
|
||||
@param cryptoID: cryptoID for server authentication. Mutually
|
||||
exclusive with the 'x509...' arguments.
|
||||
|
||||
@type protocol: str
|
||||
@param protocol: cryptoID protocol URI for server
|
||||
authentication. Requires the 'cryptoID' argument.
|
||||
|
||||
@type x509Fingerprint: str
|
||||
@param x509Fingerprint: Hex-encoded X.509 fingerprint for
|
||||
server authentication. Mutually exclusive with the 'cryptoID'
|
||||
and 'x509TrustList' arguments.
|
||||
|
||||
@type x509TrustList: list of L{tlslite.X509.X509}
|
||||
@param x509TrustList: A list of trusted root certificates. The
|
||||
other party must present a certificate chain which extends to
|
||||
one of these root certificates. The cryptlib_py module must be
|
||||
installed to use this parameter. Mutually exclusive with the
|
||||
'cryptoID' and 'x509Fingerprint' arguments.
|
||||
|
||||
@type x509CommonName: str
|
||||
@param x509CommonName: The end-entity certificate's 'CN' field
|
||||
must match this value. For a web server, this is typically a
|
||||
server name such as 'www.amazon.com'. Mutually exclusive with
|
||||
the 'cryptoID' and 'x509Fingerprint' arguments. Requires the
|
||||
'x509TrustList' argument.
|
||||
|
||||
@type settings: L{tlslite.HandshakeSettings.HandshakeSettings}
|
||||
@param settings: Various settings which can be used to control
|
||||
the ciphersuites, certificate types, and SSL/TLS versions
|
||||
offered by the client.
|
||||
"""
|
||||
|
||||
ClientHelper.__init__(self,
|
||||
username, password, sharedKey,
|
||||
certChain, privateKey,
|
||||
cryptoID, protocol,
|
||||
x509Fingerprint,
|
||||
x509TrustList, x509CommonName,
|
||||
settings)
|
||||
|
||||
|
||||
def make_connection(self, host):
|
||||
# create a HTTPS connection object from a host descriptor
|
||||
host, extra_headers, x509 = self.get_host_info(host)
|
||||
http = HTTPTLSConnection(host, None,
|
||||
self.username, self.password,
|
||||
self.sharedKey,
|
||||
self.certChain, self.privateKey,
|
||||
self.checker.cryptoID,
|
||||
self.checker.protocol,
|
||||
self.checker.x509Fingerprint,
|
||||
self.checker.x509TrustList,
|
||||
self.checker.x509CommonName,
|
||||
self.settings)
|
||||
http2 = httplib.HTTP()
|
||||
http2._setup(http)
|
||||
return http2
|
||||
@@ -1,17 +0,0 @@
|
||||
"""Classes for integrating TLS Lite with other packages."""
|
||||
|
||||
__all__ = ["AsyncStateMachine",
|
||||
"HTTPTLSConnection",
|
||||
"POP3_TLS",
|
||||
"IMAP4_TLS",
|
||||
"SMTP_TLS",
|
||||
"XMLRPCTransport",
|
||||
"TLSSocketServerMixIn",
|
||||
"TLSAsyncDispatcherMixIn",
|
||||
"TLSTwistedProtocolWrapper"]
|
||||
|
||||
try:
|
||||
import twisted
|
||||
del twisted
|
||||
except ImportError:
|
||||
del __all__[__all__.index("TLSTwistedProtocolWrapper")]
|
||||
@@ -1,170 +0,0 @@
|
||||
"""Miscellaneous helper functions."""
|
||||
|
||||
from .utils.compat import *
|
||||
from .utils.cryptomath import *
|
||||
|
||||
import hmac
|
||||
import md5
|
||||
import sha
|
||||
|
||||
#1024, 1536, 2048, 3072, 4096, 6144, and 8192 bit groups]
|
||||
goodGroupParameters = [(2,0xEEAF0AB9ADB38DD69C33F80AFA8FC5E86072618775FF3C0B9EA2314C9C256576D674DF7496EA81D3383B4813D692C6E0E0D5D8E250B98BE48E495C1D6089DAD15DC7D7B46154D6B6CE8EF4AD69B15D4982559B297BCF1885C529F566660E57EC68EDBC3C05726CC02FD4CBF4976EAA9AFD5138FE8376435B9FC61D2FC0EB06E3),\
|
||||
(2,0x9DEF3CAFB939277AB1F12A8617A47BBBDBA51DF499AC4C80BEEEA9614B19CC4D5F4F5F556E27CBDE51C6A94BE4607A291558903BA0D0F84380B655BB9A22E8DCDF028A7CEC67F0D08134B1C8B97989149B609E0BE3BAB63D47548381DBC5B1FC764E3F4B53DD9DA1158BFD3E2B9C8CF56EDF019539349627DB2FD53D24B7C48665772E437D6C7F8CE442734AF7CCB7AE837C264AE3A9BEB87F8A2FE9B8B5292E5A021FFF5E91479E8CE7A28C2442C6F315180F93499A234DCF76E3FED135F9BB),\
|
||||
(2,0xAC6BDB41324A9A9BF166DE5E1389582FAF72B6651987EE07FC3192943DB56050A37329CBB4A099ED8193E0757767A13DD52312AB4B03310DCD7F48A9DA04FD50E8083969EDB767B0CF6095179A163AB3661A05FBD5FAAAE82918A9962F0B93B855F97993EC975EEAA80D740ADBF4FF747359D041D5C33EA71D281E446B14773BCA97B43A23FB801676BD207A436C6481F1D2B9078717461A5B9D32E688F87748544523B524B0D57D5EA77A2775D2ECFA032CFBDBF52FB3786160279004E57AE6AF874E7303CE53299CCC041C7BC308D82A5698F3A8D0C38271AE35F8E9DBFBB694B5C803D89F7AE435DE236D525F54759B65E372FCD68EF20FA7111F9E4AFF73),\
|
||||
(2,0xFFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3DC2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F83655D23DCA3AD961C62F356208552BB9ED529077096966D670C354E4ABC9804F1746C08CA18217C32905E462E36CE3BE39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9DE2BCBF6955817183995497CEA956AE515D2261898FA051015728E5A8AAAC42DAD33170D04507A33A85521ABDF1CBA64ECFB850458DBEF0A8AEA71575D060C7DB3970F85A6E1E4C7ABF5AE8CDB0933D71E8C94E04A25619DCEE3D2261AD2EE6BF12FFA06D98A0864D87602733EC86A64521F2B18177B200CBBE117577A615D6C770988C0BAD946E208E24FA074E5AB3143DB5BFCE0FD108E4B82D120A93AD2CAFFFFFFFFFFFFFFFF),\
|
||||
(5,0xFFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3DC2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F83655D23DCA3AD961C62F356208552BB9ED529077096966D670C354E4ABC9804F1746C08CA18217C32905E462E36CE3BE39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9DE2BCBF6955817183995497CEA956AE515D2261898FA051015728E5A8AAAC42DAD33170D04507A33A85521ABDF1CBA64ECFB850458DBEF0A8AEA71575D060C7DB3970F85A6E1E4C7ABF5AE8CDB0933D71E8C94E04A25619DCEE3D2261AD2EE6BF12FFA06D98A0864D87602733EC86A64521F2B18177B200CBBE117577A615D6C770988C0BAD946E208E24FA074E5AB3143DB5BFCE0FD108E4B82D120A92108011A723C12A787E6D788719A10BDBA5B2699C327186AF4E23C1A946834B6150BDA2583E9CA2AD44CE8DBBBC2DB04DE8EF92E8EFC141FBECAA6287C59474E6BC05D99B2964FA090C3A2233BA186515BE7ED1F612970CEE2D7AFB81BDD762170481CD0069127D5B05AA993B4EA988D8FDDC186FFB7DC90A6C08F4DF435C934063199FFFFFFFFFFFFFFFF),\
|
||||
(5,0xFFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3DC2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F83655D23DCA3AD961C62F356208552BB9ED529077096966D670C354E4ABC9804F1746C08CA18217C32905E462E36CE3BE39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9DE2BCBF6955817183995497CEA956AE515D2261898FA051015728E5A8AAAC42DAD33170D04507A33A85521ABDF1CBA64ECFB850458DBEF0A8AEA71575D060C7DB3970F85A6E1E4C7ABF5AE8CDB0933D71E8C94E04A25619DCEE3D2261AD2EE6BF12FFA06D98A0864D87602733EC86A64521F2B18177B200CBBE117577A615D6C770988C0BAD946E208E24FA074E5AB3143DB5BFCE0FD108E4B82D120A92108011A723C12A787E6D788719A10BDBA5B2699C327186AF4E23C1A946834B6150BDA2583E9CA2AD44CE8DBBBC2DB04DE8EF92E8EFC141FBECAA6287C59474E6BC05D99B2964FA090C3A2233BA186515BE7ED1F612970CEE2D7AFB81BDD762170481CD0069127D5B05AA993B4EA988D8FDDC186FFB7DC90A6C08F4DF435C93402849236C3FAB4D27C7026C1D4DCB2602646DEC9751E763DBA37BDF8FF9406AD9E530EE5DB382F413001AEB06A53ED9027D831179727B0865A8918DA3EDBEBCF9B14ED44CE6CBACED4BB1BDB7F1447E6CC254B332051512BD7AF426FB8F401378CD2BF5983CA01C64B92ECF032EA15D1721D03F482D7CE6E74FEF6D55E702F46980C82B5A84031900B1C9E59E7C97FBEC7E8F323A97A7E36CC88BE0F1D45B7FF585AC54BD407B22B4154AACC8F6D7EBF48E1D814CC5ED20F8037E0A79715EEF29BE32806A1D58BB7C5DA76F550AA3D8A1FBFF0EB19CCB1A313D55CDA56C9EC2EF29632387FE8D76E3C0468043E8F663F4860EE12BF2D5B0B7474D6E694F91E6DCC4024FFFFFFFFFFFFFFFF),\
|
||||
(5,0xFFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3DC2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F83655D23DCA3AD961C62F356208552BB9ED529077096966D670C354E4ABC9804F1746C08CA18217C32905E462E36CE3BE39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9DE2BCBF6955817183995497CEA956AE515D2261898FA051015728E5A8AAAC42DAD33170D04507A33A85521ABDF1CBA64ECFB850458DBEF0A8AEA71575D060C7DB3970F85A6E1E4C7ABF5AE8CDB0933D71E8C94E04A25619DCEE3D2261AD2EE6BF12FFA06D98A0864D87602733EC86A64521F2B18177B200CBBE117577A615D6C770988C0BAD946E208E24FA074E5AB3143DB5BFCE0FD108E4B82D120A92108011A723C12A787E6D788719A10BDBA5B2699C327186AF4E23C1A946834B6150BDA2583E9CA2AD44CE8DBBBC2DB04DE8EF92E8EFC141FBECAA6287C59474E6BC05D99B2964FA090C3A2233BA186515BE7ED1F612970CEE2D7AFB81BDD762170481CD0069127D5B05AA993B4EA988D8FDDC186FFB7DC90A6C08F4DF435C93402849236C3FAB4D27C7026C1D4DCB2602646DEC9751E763DBA37BDF8FF9406AD9E530EE5DB382F413001AEB06A53ED9027D831179727B0865A8918DA3EDBEBCF9B14ED44CE6CBACED4BB1BDB7F1447E6CC254B332051512BD7AF426FB8F401378CD2BF5983CA01C64B92ECF032EA15D1721D03F482D7CE6E74FEF6D55E702F46980C82B5A84031900B1C9E59E7C97FBEC7E8F323A97A7E36CC88BE0F1D45B7FF585AC54BD407B22B4154AACC8F6D7EBF48E1D814CC5ED20F8037E0A79715EEF29BE32806A1D58BB7C5DA76F550AA3D8A1FBFF0EB19CCB1A313D55CDA56C9EC2EF29632387FE8D76E3C0468043E8F663F4860EE12BF2D5B0B7474D6E694F91E6DBE115974A3926F12FEE5E438777CB6A932DF8CD8BEC4D073B931BA3BC832B68D9DD300741FA7BF8AFC47ED2576F6936BA424663AAB639C5AE4F5683423B4742BF1C978238F16CBE39D652DE3FDB8BEFC848AD922222E04A4037C0713EB57A81A23F0C73473FC646CEA306B4BCBC8862F8385DDFA9D4B7FA2C087E879683303ED5BDD3A062B3CF5B3A278A66D2A13F83F44F82DDF310EE074AB6A364597E899A0255DC164F31CC50846851DF9AB48195DED7EA1B1D510BD7EE74D73FAF36BC31ECFA268359046F4EB879F924009438B481C6CD7889A002ED5EE382BC9190DA6FC026E479558E4475677E9AA9E3050E2765694DFC81F56E880B96E7160C980DD98EDD3DFFFFFFFFFFFFFFFFF)]
|
||||
|
||||
def P_hash(hashModule, secret, seed, length):
|
||||
bytes = createByteArrayZeros(length)
|
||||
secret = bytesToString(secret)
|
||||
seed = bytesToString(seed)
|
||||
A = seed
|
||||
index = 0
|
||||
while 1:
|
||||
A = hmac.HMAC(secret, A, hashModule).digest()
|
||||
output = hmac.HMAC(secret, A+seed, hashModule).digest()
|
||||
for c in output:
|
||||
if index >= length:
|
||||
return bytes
|
||||
bytes[index] = ord(c)
|
||||
index += 1
|
||||
return bytes
|
||||
|
||||
def PRF(secret, label, seed, length):
|
||||
#Split the secret into left and right halves
|
||||
S1 = secret[ : int(math.ceil(len(secret)/2.0))]
|
||||
S2 = secret[ int(math.floor(len(secret)/2.0)) : ]
|
||||
|
||||
#Run the left half through P_MD5 and the right half through P_SHA1
|
||||
p_md5 = P_hash(md5, S1, concatArrays(stringToBytes(label), seed), length)
|
||||
p_sha1 = P_hash(sha, S2, concatArrays(stringToBytes(label), seed), length)
|
||||
|
||||
#XOR the output values and return the result
|
||||
for x in range(length):
|
||||
p_md5[x] ^= p_sha1[x]
|
||||
return p_md5
|
||||
|
||||
|
||||
def PRF_SSL(secret, seed, length):
|
||||
secretStr = bytesToString(secret)
|
||||
seedStr = bytesToString(seed)
|
||||
bytes = createByteArrayZeros(length)
|
||||
index = 0
|
||||
for x in range(26):
|
||||
A = chr(ord('A')+x) * (x+1) # 'A', 'BB', 'CCC', etc..
|
||||
input = secretStr + sha.sha(A + secretStr + seedStr).digest()
|
||||
output = md5.md5(input).digest()
|
||||
for c in output:
|
||||
if index >= length:
|
||||
return bytes
|
||||
bytes[index] = ord(c)
|
||||
index += 1
|
||||
return bytes
|
||||
|
||||
def makeX(salt, username, password):
|
||||
if len(username)>=256:
|
||||
raise ValueError("username too long")
|
||||
if len(salt)>=256:
|
||||
raise ValueError("salt too long")
|
||||
return stringToNumber(sha.sha(salt + sha.sha(username + ":" + password)\
|
||||
.digest()).digest())
|
||||
|
||||
#This function is used by VerifierDB.makeVerifier
|
||||
def makeVerifier(username, password, bits):
|
||||
bitsIndex = {1024:0, 1536:1, 2048:2, 3072:3, 4096:4, 6144:5, 8192:6}[bits]
|
||||
g,N = goodGroupParameters[bitsIndex]
|
||||
salt = bytesToString(getRandomBytes(16))
|
||||
x = makeX(salt, username, password)
|
||||
verifier = powMod(g, x, N)
|
||||
return N, g, salt, verifier
|
||||
|
||||
def PAD(n, x):
|
||||
nLength = len(numberToString(n))
|
||||
s = numberToString(x)
|
||||
if len(s) < nLength:
|
||||
s = ("\0" * (nLength-len(s))) + s
|
||||
return s
|
||||
|
||||
def makeU(N, A, B):
|
||||
return stringToNumber(sha.sha(PAD(N, A) + PAD(N, B)).digest())
|
||||
|
||||
def makeK(N, g):
|
||||
return stringToNumber(sha.sha(numberToString(N) + PAD(N, g)).digest())
|
||||
|
||||
|
||||
"""
|
||||
MAC_SSL
|
||||
Modified from Python HMAC by Trevor
|
||||
"""
|
||||
|
||||
class MAC_SSL:
|
||||
"""MAC_SSL class.
|
||||
|
||||
This supports the API for Cryptographic Hash Functions (PEP 247).
|
||||
"""
|
||||
|
||||
def __init__(self, key, msg = None, digestmod = None):
|
||||
"""Create a new MAC_SSL object.
|
||||
|
||||
key: key for the keyed hash object.
|
||||
msg: Initial input for the hash, if provided.
|
||||
digestmod: A module supporting PEP 247. Defaults to the md5 module.
|
||||
"""
|
||||
if digestmod is None:
|
||||
import md5
|
||||
digestmod = md5
|
||||
|
||||
if key == None: #TREVNEW - for faster copying
|
||||
return #TREVNEW
|
||||
|
||||
self.digestmod = digestmod
|
||||
self.outer = digestmod.new()
|
||||
self.inner = digestmod.new()
|
||||
self.digest_size = digestmod.digest_size
|
||||
|
||||
ipad = "\x36" * 40
|
||||
opad = "\x5C" * 40
|
||||
|
||||
self.inner.update(key)
|
||||
self.inner.update(ipad)
|
||||
self.outer.update(key)
|
||||
self.outer.update(opad)
|
||||
if msg is not None:
|
||||
self.update(msg)
|
||||
|
||||
|
||||
def update(self, msg):
|
||||
"""Update this hashing object with the string msg.
|
||||
"""
|
||||
self.inner.update(msg)
|
||||
|
||||
def copy(self):
|
||||
"""Return a separate copy of this hashing object.
|
||||
|
||||
An update to this copy won't affect the original object.
|
||||
"""
|
||||
other = MAC_SSL(None) #TREVNEW - for faster copying
|
||||
other.digest_size = self.digest_size #TREVNEW
|
||||
other.digestmod = self.digestmod
|
||||
other.inner = self.inner.copy()
|
||||
other.outer = self.outer.copy()
|
||||
return other
|
||||
|
||||
def digest(self):
|
||||
"""Return the hash value of this hashing object.
|
||||
|
||||
This returns a string containing 8-bit data. The object is
|
||||
not altered in any way by this function; you can continue
|
||||
updating the object after calling this function.
|
||||
"""
|
||||
h = self.outer.copy()
|
||||
h.update(self.inner.digest())
|
||||
return h.digest()
|
||||
|
||||
def hexdigest(self):
|
||||
"""Like digest(), but returns a string of hexadecimal digits instead.
|
||||
"""
|
||||
return "".join([hex(ord(x))[2:].zfill(2)
|
||||
for x in tuple(self.digest())])
|
||||
@@ -1,561 +0,0 @@
|
||||
"""Classes representing TLS messages."""
|
||||
|
||||
from .utils.compat import *
|
||||
from .utils.cryptomath import *
|
||||
from .errors import *
|
||||
from .utils.codec import *
|
||||
from .constants import *
|
||||
from .X509 import X509
|
||||
from .X509CertChain import X509CertChain
|
||||
|
||||
import sha
|
||||
import md5
|
||||
|
||||
class RecordHeader3:
|
||||
def __init__(self):
|
||||
self.type = 0
|
||||
self.version = (0,0)
|
||||
self.length = 0
|
||||
self.ssl2 = False
|
||||
|
||||
def create(self, version, type, length):
|
||||
self.type = type
|
||||
self.version = version
|
||||
self.length = length
|
||||
return self
|
||||
|
||||
def write(self):
|
||||
w = Writer(5)
|
||||
w.add(self.type, 1)
|
||||
w.add(self.version[0], 1)
|
||||
w.add(self.version[1], 1)
|
||||
w.add(self.length, 2)
|
||||
return w.bytes
|
||||
|
||||
def parse(self, p):
|
||||
self.type = p.get(1)
|
||||
self.version = (p.get(1), p.get(1))
|
||||
self.length = p.get(2)
|
||||
self.ssl2 = False
|
||||
return self
|
||||
|
||||
class RecordHeader2:
|
||||
def __init__(self):
|
||||
self.type = 0
|
||||
self.version = (0,0)
|
||||
self.length = 0
|
||||
self.ssl2 = True
|
||||
|
||||
def parse(self, p):
|
||||
if p.get(1)!=128:
|
||||
raise SyntaxError()
|
||||
self.type = ContentType.handshake
|
||||
self.version = (2,0)
|
||||
#We don't support 2-byte-length-headers; could be a problem
|
||||
self.length = p.get(1)
|
||||
return self
|
||||
|
||||
|
||||
class Msg:
|
||||
def preWrite(self, trial):
|
||||
if trial:
|
||||
w = Writer()
|
||||
else:
|
||||
length = self.write(True)
|
||||
w = Writer(length)
|
||||
return w
|
||||
|
||||
def postWrite(self, w, trial):
|
||||
if trial:
|
||||
return w.index
|
||||
else:
|
||||
return w.bytes
|
||||
|
||||
class Alert(Msg):
|
||||
def __init__(self):
|
||||
self.contentType = ContentType.alert
|
||||
self.level = 0
|
||||
self.description = 0
|
||||
|
||||
def create(self, description, level=AlertLevel.fatal):
|
||||
self.level = level
|
||||
self.description = description
|
||||
return self
|
||||
|
||||
def parse(self, p):
|
||||
p.setLengthCheck(2)
|
||||
self.level = p.get(1)
|
||||
self.description = p.get(1)
|
||||
p.stopLengthCheck()
|
||||
return self
|
||||
|
||||
def write(self):
|
||||
w = Writer(2)
|
||||
w.add(self.level, 1)
|
||||
w.add(self.description, 1)
|
||||
return w.bytes
|
||||
|
||||
|
||||
class HandshakeMsg(Msg):
|
||||
def preWrite(self, handshakeType, trial):
|
||||
if trial:
|
||||
w = Writer()
|
||||
w.add(handshakeType, 1)
|
||||
w.add(0, 3)
|
||||
else:
|
||||
length = self.write(True)
|
||||
w = Writer(length)
|
||||
w.add(handshakeType, 1)
|
||||
w.add(length-4, 3)
|
||||
return w
|
||||
|
||||
|
||||
class ClientHello(HandshakeMsg):
|
||||
def __init__(self, ssl2=False):
|
||||
self.contentType = ContentType.handshake
|
||||
self.ssl2 = ssl2
|
||||
self.client_version = (0,0)
|
||||
self.random = createByteArrayZeros(32)
|
||||
self.session_id = createByteArraySequence([])
|
||||
self.cipher_suites = [] # a list of 16-bit values
|
||||
self.certificate_types = [CertificateType.x509]
|
||||
self.compression_methods = [] # a list of 8-bit values
|
||||
self.srp_username = None # a string
|
||||
|
||||
def create(self, version, random, session_id, cipher_suites,
|
||||
certificate_types=None, srp_username=None):
|
||||
self.client_version = version
|
||||
self.random = random
|
||||
self.session_id = session_id
|
||||
self.cipher_suites = cipher_suites
|
||||
self.certificate_types = certificate_types
|
||||
self.compression_methods = [0]
|
||||
self.srp_username = srp_username
|
||||
return self
|
||||
|
||||
def parse(self, p):
|
||||
if self.ssl2:
|
||||
self.client_version = (p.get(1), p.get(1))
|
||||
cipherSpecsLength = p.get(2)
|
||||
sessionIDLength = p.get(2)
|
||||
randomLength = p.get(2)
|
||||
self.cipher_suites = p.getFixList(3, int(cipherSpecsLength/3))
|
||||
self.session_id = p.getFixBytes(sessionIDLength)
|
||||
self.random = p.getFixBytes(randomLength)
|
||||
if len(self.random) < 32:
|
||||
zeroBytes = 32-len(self.random)
|
||||
self.random = createByteArrayZeros(zeroBytes) + self.random
|
||||
self.compression_methods = [0]#Fake this value
|
||||
|
||||
#We're not doing a stopLengthCheck() for SSLv2, oh well..
|
||||
else:
|
||||
p.startLengthCheck(3)
|
||||
self.client_version = (p.get(1), p.get(1))
|
||||
self.random = p.getFixBytes(32)
|
||||
self.session_id = p.getVarBytes(1)
|
||||
self.cipher_suites = p.getVarList(2, 2)
|
||||
self.compression_methods = p.getVarList(1, 1)
|
||||
if not p.atLengthCheck():
|
||||
totalExtLength = p.get(2)
|
||||
soFar = 0
|
||||
while soFar != totalExtLength:
|
||||
extType = p.get(2)
|
||||
extLength = p.get(2)
|
||||
if extType == 6:
|
||||
self.srp_username = bytesToString(p.getVarBytes(1))
|
||||
elif extType == 7:
|
||||
self.certificate_types = p.getVarList(1, 1)
|
||||
else:
|
||||
p.getFixBytes(extLength)
|
||||
soFar += 4 + extLength
|
||||
p.stopLengthCheck()
|
||||
return self
|
||||
|
||||
def write(self, trial=False):
|
||||
w = HandshakeMsg.preWrite(self, HandshakeType.client_hello, trial)
|
||||
w.add(self.client_version[0], 1)
|
||||
w.add(self.client_version[1], 1)
|
||||
w.addFixSeq(self.random, 1)
|
||||
w.addVarSeq(self.session_id, 1, 1)
|
||||
w.addVarSeq(self.cipher_suites, 2, 2)
|
||||
w.addVarSeq(self.compression_methods, 1, 1)
|
||||
|
||||
extLength = 0
|
||||
if self.certificate_types and self.certificate_types != \
|
||||
[CertificateType.x509]:
|
||||
extLength += 5 + len(self.certificate_types)
|
||||
if self.srp_username:
|
||||
extLength += 5 + len(self.srp_username)
|
||||
if extLength > 0:
|
||||
w.add(extLength, 2)
|
||||
|
||||
if self.certificate_types and self.certificate_types != \
|
||||
[CertificateType.x509]:
|
||||
w.add(7, 2)
|
||||
w.add(len(self.certificate_types)+1, 2)
|
||||
w.addVarSeq(self.certificate_types, 1, 1)
|
||||
if self.srp_username:
|
||||
w.add(6, 2)
|
||||
w.add(len(self.srp_username)+1, 2)
|
||||
w.addVarSeq(stringToBytes(self.srp_username), 1, 1)
|
||||
|
||||
return HandshakeMsg.postWrite(self, w, trial)
|
||||
|
||||
|
||||
class ServerHello(HandshakeMsg):
|
||||
def __init__(self):
|
||||
self.contentType = ContentType.handshake
|
||||
self.server_version = (0,0)
|
||||
self.random = createByteArrayZeros(32)
|
||||
self.session_id = createByteArraySequence([])
|
||||
self.cipher_suite = 0
|
||||
self.certificate_type = CertificateType.x509
|
||||
self.compression_method = 0
|
||||
|
||||
def create(self, version, random, session_id, cipher_suite,
|
||||
certificate_type):
|
||||
self.server_version = version
|
||||
self.random = random
|
||||
self.session_id = session_id
|
||||
self.cipher_suite = cipher_suite
|
||||
self.certificate_type = certificate_type
|
||||
self.compression_method = 0
|
||||
return self
|
||||
|
||||
def parse(self, p):
|
||||
p.startLengthCheck(3)
|
||||
self.server_version = (p.get(1), p.get(1))
|
||||
self.random = p.getFixBytes(32)
|
||||
self.session_id = p.getVarBytes(1)
|
||||
self.cipher_suite = p.get(2)
|
||||
self.compression_method = p.get(1)
|
||||
if not p.atLengthCheck():
|
||||
totalExtLength = p.get(2)
|
||||
soFar = 0
|
||||
while soFar != totalExtLength:
|
||||
extType = p.get(2)
|
||||
extLength = p.get(2)
|
||||
if extType == 7:
|
||||
self.certificate_type = p.get(1)
|
||||
else:
|
||||
p.getFixBytes(extLength)
|
||||
soFar += 4 + extLength
|
||||
p.stopLengthCheck()
|
||||
return self
|
||||
|
||||
def write(self, trial=False):
|
||||
w = HandshakeMsg.preWrite(self, HandshakeType.server_hello, trial)
|
||||
w.add(self.server_version[0], 1)
|
||||
w.add(self.server_version[1], 1)
|
||||
w.addFixSeq(self.random, 1)
|
||||
w.addVarSeq(self.session_id, 1, 1)
|
||||
w.add(self.cipher_suite, 2)
|
||||
w.add(self.compression_method, 1)
|
||||
|
||||
extLength = 0
|
||||
if self.certificate_type and self.certificate_type != \
|
||||
CertificateType.x509:
|
||||
extLength += 5
|
||||
|
||||
if extLength != 0:
|
||||
w.add(extLength, 2)
|
||||
|
||||
if self.certificate_type and self.certificate_type != \
|
||||
CertificateType.x509:
|
||||
w.add(7, 2)
|
||||
w.add(1, 2)
|
||||
w.add(self.certificate_type, 1)
|
||||
|
||||
return HandshakeMsg.postWrite(self, w, trial)
|
||||
|
||||
class Certificate(HandshakeMsg):
|
||||
def __init__(self, certificateType):
|
||||
self.certificateType = certificateType
|
||||
self.contentType = ContentType.handshake
|
||||
self.certChain = None
|
||||
|
||||
def create(self, certChain):
|
||||
self.certChain = certChain
|
||||
return self
|
||||
|
||||
def parse(self, p):
|
||||
p.startLengthCheck(3)
|
||||
if self.certificateType == CertificateType.x509:
|
||||
chainLength = p.get(3)
|
||||
index = 0
|
||||
certificate_list = []
|
||||
while index != chainLength:
|
||||
certBytes = p.getVarBytes(3)
|
||||
x509 = X509()
|
||||
x509.parseBinary(certBytes)
|
||||
certificate_list.append(x509)
|
||||
index += len(certBytes)+3
|
||||
if certificate_list:
|
||||
self.certChain = X509CertChain(certificate_list)
|
||||
elif self.certificateType == CertificateType.cryptoID:
|
||||
s = bytesToString(p.getVarBytes(2))
|
||||
if s:
|
||||
try:
|
||||
import cryptoIDlib.CertChain
|
||||
except ImportError:
|
||||
raise SyntaxError(\
|
||||
"cryptoID cert chain received, cryptoIDlib not present")
|
||||
self.certChain = cryptoIDlib.CertChain.CertChain().parse(s)
|
||||
else:
|
||||
raise AssertionError()
|
||||
|
||||
p.stopLengthCheck()
|
||||
return self
|
||||
|
||||
def write(self, trial=False):
|
||||
w = HandshakeMsg.preWrite(self, HandshakeType.certificate, trial)
|
||||
if self.certificateType == CertificateType.x509:
|
||||
chainLength = 0
|
||||
if self.certChain:
|
||||
certificate_list = self.certChain.x509List
|
||||
else:
|
||||
certificate_list = []
|
||||
#determine length
|
||||
for cert in certificate_list:
|
||||
bytes = cert.writeBytes()
|
||||
chainLength += len(bytes)+3
|
||||
#add bytes
|
||||
w.add(chainLength, 3)
|
||||
for cert in certificate_list:
|
||||
bytes = cert.writeBytes()
|
||||
w.addVarSeq(bytes, 1, 3)
|
||||
elif self.certificateType == CertificateType.cryptoID:
|
||||
if self.certChain:
|
||||
bytes = stringToBytes(self.certChain.write())
|
||||
else:
|
||||
bytes = createByteArraySequence([])
|
||||
w.addVarSeq(bytes, 1, 2)
|
||||
else:
|
||||
raise AssertionError()
|
||||
return HandshakeMsg.postWrite(self, w, trial)
|
||||
|
||||
class CertificateRequest(HandshakeMsg):
|
||||
def __init__(self):
|
||||
self.contentType = ContentType.handshake
|
||||
self.certificate_types = []
|
||||
#treat as opaque bytes for now
|
||||
self.certificate_authorities = createByteArraySequence([])
|
||||
|
||||
def create(self, certificate_types, certificate_authorities):
|
||||
self.certificate_types = certificate_types
|
||||
self.certificate_authorities = certificate_authorities
|
||||
return self
|
||||
|
||||
def parse(self, p):
|
||||
p.startLengthCheck(3)
|
||||
self.certificate_types = p.getVarList(1, 1)
|
||||
self.certificate_authorities = p.getVarBytes(2)
|
||||
p.stopLengthCheck()
|
||||
return self
|
||||
|
||||
def write(self, trial=False):
|
||||
w = HandshakeMsg.preWrite(self, HandshakeType.certificate_request,
|
||||
trial)
|
||||
w.addVarSeq(self.certificate_types, 1, 1)
|
||||
w.addVarSeq(self.certificate_authorities, 1, 2)
|
||||
return HandshakeMsg.postWrite(self, w, trial)
|
||||
|
||||
class ServerKeyExchange(HandshakeMsg):
|
||||
def __init__(self, cipherSuite):
|
||||
self.cipherSuite = cipherSuite
|
||||
self.contentType = ContentType.handshake
|
||||
self.srp_N = 0
|
||||
self.srp_g = 0
|
||||
self.srp_s = createByteArraySequence([])
|
||||
self.srp_B = 0
|
||||
self.signature = createByteArraySequence([])
|
||||
|
||||
def createSRP(self, srp_N, srp_g, srp_s, srp_B):
|
||||
self.srp_N = srp_N
|
||||
self.srp_g = srp_g
|
||||
self.srp_s = srp_s
|
||||
self.srp_B = srp_B
|
||||
return self
|
||||
|
||||
def parse(self, p):
|
||||
p.startLengthCheck(3)
|
||||
self.srp_N = bytesToNumber(p.getVarBytes(2))
|
||||
self.srp_g = bytesToNumber(p.getVarBytes(2))
|
||||
self.srp_s = p.getVarBytes(1)
|
||||
self.srp_B = bytesToNumber(p.getVarBytes(2))
|
||||
if self.cipherSuite in CipherSuite.srpRsaSuites:
|
||||
self.signature = p.getVarBytes(2)
|
||||
p.stopLengthCheck()
|
||||
return self
|
||||
|
||||
def write(self, trial=False):
|
||||
w = HandshakeMsg.preWrite(self, HandshakeType.server_key_exchange,
|
||||
trial)
|
||||
w.addVarSeq(numberToBytes(self.srp_N), 1, 2)
|
||||
w.addVarSeq(numberToBytes(self.srp_g), 1, 2)
|
||||
w.addVarSeq(self.srp_s, 1, 1)
|
||||
w.addVarSeq(numberToBytes(self.srp_B), 1, 2)
|
||||
if self.cipherSuite in CipherSuite.srpRsaSuites:
|
||||
w.addVarSeq(self.signature, 1, 2)
|
||||
return HandshakeMsg.postWrite(self, w, trial)
|
||||
|
||||
def hash(self, clientRandom, serverRandom):
|
||||
oldCipherSuite = self.cipherSuite
|
||||
self.cipherSuite = None
|
||||
try:
|
||||
bytes = clientRandom + serverRandom + self.write()[4:]
|
||||
s = bytesToString(bytes)
|
||||
return stringToBytes(md5.md5(s).digest() + sha.sha(s).digest())
|
||||
finally:
|
||||
self.cipherSuite = oldCipherSuite
|
||||
|
||||
class ServerHelloDone(HandshakeMsg):
|
||||
def __init__(self):
|
||||
self.contentType = ContentType.handshake
|
||||
|
||||
def create(self):
|
||||
return self
|
||||
|
||||
def parse(self, p):
|
||||
p.startLengthCheck(3)
|
||||
p.stopLengthCheck()
|
||||
return self
|
||||
|
||||
def write(self, trial=False):
|
||||
w = HandshakeMsg.preWrite(self, HandshakeType.server_hello_done, trial)
|
||||
return HandshakeMsg.postWrite(self, w, trial)
|
||||
|
||||
class ClientKeyExchange(HandshakeMsg):
|
||||
def __init__(self, cipherSuite, version=None):
|
||||
self.cipherSuite = cipherSuite
|
||||
self.version = version
|
||||
self.contentType = ContentType.handshake
|
||||
self.srp_A = 0
|
||||
self.encryptedPreMasterSecret = createByteArraySequence([])
|
||||
|
||||
def createSRP(self, srp_A):
|
||||
self.srp_A = srp_A
|
||||
return self
|
||||
|
||||
def createRSA(self, encryptedPreMasterSecret):
|
||||
self.encryptedPreMasterSecret = encryptedPreMasterSecret
|
||||
return self
|
||||
|
||||
def parse(self, p):
|
||||
p.startLengthCheck(3)
|
||||
if self.cipherSuite in CipherSuite.srpSuites + \
|
||||
CipherSuite.srpRsaSuites:
|
||||
self.srp_A = bytesToNumber(p.getVarBytes(2))
|
||||
elif self.cipherSuite in CipherSuite.rsaSuites:
|
||||
if self.version in ((3,1), (3,2)):
|
||||
self.encryptedPreMasterSecret = p.getVarBytes(2)
|
||||
elif self.version == (3,0):
|
||||
self.encryptedPreMasterSecret = \
|
||||
p.getFixBytes(len(p.bytes)-p.index)
|
||||
else:
|
||||
raise AssertionError()
|
||||
else:
|
||||
raise AssertionError()
|
||||
p.stopLengthCheck()
|
||||
return self
|
||||
|
||||
def write(self, trial=False):
|
||||
w = HandshakeMsg.preWrite(self, HandshakeType.client_key_exchange,
|
||||
trial)
|
||||
if self.cipherSuite in CipherSuite.srpSuites + \
|
||||
CipherSuite.srpRsaSuites:
|
||||
w.addVarSeq(numberToBytes(self.srp_A), 1, 2)
|
||||
elif self.cipherSuite in CipherSuite.rsaSuites:
|
||||
if self.version in ((3,1), (3,2)):
|
||||
w.addVarSeq(self.encryptedPreMasterSecret, 1, 2)
|
||||
elif self.version == (3,0):
|
||||
w.addFixSeq(self.encryptedPreMasterSecret, 1)
|
||||
else:
|
||||
raise AssertionError()
|
||||
else:
|
||||
raise AssertionError()
|
||||
return HandshakeMsg.postWrite(self, w, trial)
|
||||
|
||||
class CertificateVerify(HandshakeMsg):
|
||||
def __init__(self):
|
||||
self.contentType = ContentType.handshake
|
||||
self.signature = createByteArraySequence([])
|
||||
|
||||
def create(self, signature):
|
||||
self.signature = signature
|
||||
return self
|
||||
|
||||
def parse(self, p):
|
||||
p.startLengthCheck(3)
|
||||
self.signature = p.getVarBytes(2)
|
||||
p.stopLengthCheck()
|
||||
return self
|
||||
|
||||
def write(self, trial=False):
|
||||
w = HandshakeMsg.preWrite(self, HandshakeType.certificate_verify,
|
||||
trial)
|
||||
w.addVarSeq(self.signature, 1, 2)
|
||||
return HandshakeMsg.postWrite(self, w, trial)
|
||||
|
||||
class ChangeCipherSpec(Msg):
|
||||
def __init__(self):
|
||||
self.contentType = ContentType.change_cipher_spec
|
||||
self.type = 1
|
||||
|
||||
def create(self):
|
||||
self.type = 1
|
||||
return self
|
||||
|
||||
def parse(self, p):
|
||||
p.setLengthCheck(1)
|
||||
self.type = p.get(1)
|
||||
p.stopLengthCheck()
|
||||
return self
|
||||
|
||||
def write(self, trial=False):
|
||||
w = Msg.preWrite(self, trial)
|
||||
w.add(self.type,1)
|
||||
return Msg.postWrite(self, w, trial)
|
||||
|
||||
|
||||
class Finished(HandshakeMsg):
|
||||
def __init__(self, version):
|
||||
self.contentType = ContentType.handshake
|
||||
self.version = version
|
||||
self.verify_data = createByteArraySequence([])
|
||||
|
||||
def create(self, verify_data):
|
||||
self.verify_data = verify_data
|
||||
return self
|
||||
|
||||
def parse(self, p):
|
||||
p.startLengthCheck(3)
|
||||
if self.version == (3,0):
|
||||
self.verify_data = p.getFixBytes(36)
|
||||
elif self.version in ((3,1), (3,2)):
|
||||
self.verify_data = p.getFixBytes(12)
|
||||
else:
|
||||
raise AssertionError()
|
||||
p.stopLengthCheck()
|
||||
return self
|
||||
|
||||
def write(self, trial=False):
|
||||
w = HandshakeMsg.preWrite(self, HandshakeType.finished, trial)
|
||||
w.addFixSeq(self.verify_data, 1)
|
||||
return HandshakeMsg.postWrite(self, w, trial)
|
||||
|
||||
class ApplicationData(Msg):
|
||||
def __init__(self):
|
||||
self.contentType = ContentType.application_data
|
||||
self.bytes = createByteArraySequence([])
|
||||
|
||||
def create(self, bytes):
|
||||
self.bytes = bytes
|
||||
return self
|
||||
|
||||
def parse(self, p):
|
||||
self.bytes = p.bytes
|
||||
return self
|
||||
|
||||
def write(self):
|
||||
return self.bytes
|
||||
@@ -1,31 +0,0 @@
|
||||
"""Abstract class for AES."""
|
||||
|
||||
class AES:
|
||||
def __init__(self, key, mode, IV, implementation):
|
||||
if len(key) not in (16, 24, 32):
|
||||
raise AssertionError()
|
||||
if mode != 2:
|
||||
raise AssertionError()
|
||||
if len(IV) != 16:
|
||||
raise AssertionError()
|
||||
self.isBlockCipher = True
|
||||
self.block_size = 16
|
||||
self.implementation = implementation
|
||||
if len(key)==16:
|
||||
self.name = "aes128"
|
||||
elif len(key)==24:
|
||||
self.name = "aes192"
|
||||
elif len(key)==32:
|
||||
self.name = "aes256"
|
||||
else:
|
||||
raise AssertionError()
|
||||
|
||||
#CBC-Mode encryption, returns ciphertext
|
||||
#WARNING: *MAY* modify the input as well
|
||||
def encrypt(self, plaintext):
|
||||
assert(len(plaintext) % 16 == 0)
|
||||
|
||||
#CBC-Mode decryption, returns plaintext
|
||||
#WARNING: *MAY* modify the input as well
|
||||
def decrypt(self, ciphertext):
|
||||
assert(len(ciphertext) % 16 == 0)
|
||||
@@ -1,34 +0,0 @@
|
||||
"""Class for parsing ASN.1"""
|
||||
from .compat import *
|
||||
from .codec import *
|
||||
|
||||
#Takes a byte array which has a DER TLV field at its head
|
||||
class ASN1Parser:
|
||||
def __init__(self, bytes):
|
||||
p = Parser(bytes)
|
||||
p.get(1) #skip Type
|
||||
|
||||
#Get Length
|
||||
self.length = self._getASN1Length(p)
|
||||
|
||||
#Get Value
|
||||
self.value = p.getFixBytes(self.length)
|
||||
|
||||
#Assuming this is a sequence...
|
||||
def getChild(self, which):
|
||||
p = Parser(self.value)
|
||||
for x in range(which+1):
|
||||
markIndex = p.index
|
||||
p.get(1) #skip Type
|
||||
length = self._getASN1Length(p)
|
||||
p.getFixBytes(length)
|
||||
return ASN1Parser(p.bytes[markIndex : p.index])
|
||||
|
||||
#Decode the ASN.1 DER length field
|
||||
def _getASN1Length(self, p):
|
||||
firstLength = p.get(1)
|
||||
if firstLength<=127:
|
||||
return firstLength
|
||||
else:
|
||||
lengthLength = firstLength & 0x7F
|
||||
return p.get(lengthLength)
|
||||
@@ -1,34 +0,0 @@
|
||||
"""Cryptlib AES implementation."""
|
||||
|
||||
from .cryptomath import *
|
||||
from .AES import *
|
||||
|
||||
if cryptlibpyLoaded:
|
||||
|
||||
def new(key, mode, IV):
|
||||
return Cryptlib_AES(key, mode, IV)
|
||||
|
||||
class Cryptlib_AES(AES):
|
||||
|
||||
def __init__(self, key, mode, IV):
|
||||
AES.__init__(self, key, mode, IV, "cryptlib")
|
||||
self.context = cryptlib_py.cryptCreateContext(cryptlib_py.CRYPT_UNUSED, cryptlib_py.CRYPT_ALGO_AES)
|
||||
cryptlib_py.cryptSetAttribute(self.context, cryptlib_py.CRYPT_CTXINFO_MODE, cryptlib_py.CRYPT_MODE_CBC)
|
||||
cryptlib_py.cryptSetAttribute(self.context, cryptlib_py.CRYPT_CTXINFO_KEYSIZE, len(key))
|
||||
cryptlib_py.cryptSetAttributeString(self.context, cryptlib_py.CRYPT_CTXINFO_KEY, key)
|
||||
cryptlib_py.cryptSetAttributeString(self.context, cryptlib_py.CRYPT_CTXINFO_IV, IV)
|
||||
|
||||
def __del__(self):
|
||||
cryptlib_py.cryptDestroyContext(self.context)
|
||||
|
||||
def encrypt(self, plaintext):
|
||||
AES.encrypt(self, plaintext)
|
||||
bytes = stringToBytes(plaintext)
|
||||
cryptlib_py.cryptEncrypt(self.context, bytes)
|
||||
return bytesToString(bytes)
|
||||
|
||||
def decrypt(self, ciphertext):
|
||||
AES.decrypt(self, ciphertext)
|
||||
bytes = stringToBytes(ciphertext)
|
||||
cryptlib_py.cryptDecrypt(self.context, bytes)
|
||||
return bytesToString(bytes)
|
||||
@@ -1,28 +0,0 @@
|
||||
"""Cryptlib RC4 implementation."""
|
||||
|
||||
from .cryptomath import *
|
||||
from .RC4 import RC4
|
||||
|
||||
if cryptlibpyLoaded:
|
||||
|
||||
def new(key):
|
||||
return Cryptlib_RC4(key)
|
||||
|
||||
class Cryptlib_RC4(RC4):
|
||||
|
||||
def __init__(self, key):
|
||||
RC4.__init__(self, key, "cryptlib")
|
||||
self.context = cryptlib_py.cryptCreateContext(cryptlib_py.CRYPT_UNUSED, cryptlib_py.CRYPT_ALGO_RC4)
|
||||
cryptlib_py.cryptSetAttribute(self.context, cryptlib_py.CRYPT_CTXINFO_KEYSIZE, len(key))
|
||||
cryptlib_py.cryptSetAttributeString(self.context, cryptlib_py.CRYPT_CTXINFO_KEY, key)
|
||||
|
||||
def __del__(self):
|
||||
cryptlib_py.cryptDestroyContext(self.context)
|
||||
|
||||
def encrypt(self, plaintext):
|
||||
bytes = stringToBytes(plaintext)
|
||||
cryptlib_py.cryptEncrypt(self.context, bytes)
|
||||
return bytesToString(bytes)
|
||||
|
||||
def decrypt(self, ciphertext):
|
||||
return self.encrypt(ciphertext)
|
||||
@@ -1,35 +0,0 @@
|
||||
"""Cryptlib 3DES implementation."""
|
||||
|
||||
from .cryptomath import *
|
||||
|
||||
from .TripleDES import *
|
||||
|
||||
if cryptlibpyLoaded:
|
||||
|
||||
def new(key, mode, IV):
|
||||
return Cryptlib_TripleDES(key, mode, IV)
|
||||
|
||||
class Cryptlib_TripleDES(TripleDES):
|
||||
|
||||
def __init__(self, key, mode, IV):
|
||||
TripleDES.__init__(self, key, mode, IV, "cryptlib")
|
||||
self.context = cryptlib_py.cryptCreateContext(cryptlib_py.CRYPT_UNUSED, cryptlib_py.CRYPT_ALGO_3DES)
|
||||
cryptlib_py.cryptSetAttribute(self.context, cryptlib_py.CRYPT_CTXINFO_MODE, cryptlib_py.CRYPT_MODE_CBC)
|
||||
cryptlib_py.cryptSetAttribute(self.context, cryptlib_py.CRYPT_CTXINFO_KEYSIZE, len(key))
|
||||
cryptlib_py.cryptSetAttributeString(self.context, cryptlib_py.CRYPT_CTXINFO_KEY, key)
|
||||
cryptlib_py.cryptSetAttributeString(self.context, cryptlib_py.CRYPT_CTXINFO_IV, IV)
|
||||
|
||||
def __del__(self):
|
||||
cryptlib_py.cryptDestroyContext(self.context)
|
||||
|
||||
def encrypt(self, plaintext):
|
||||
TripleDES.encrypt(self, plaintext)
|
||||
bytes = stringToBytes(plaintext)
|
||||
cryptlib_py.cryptEncrypt(self.context, bytes)
|
||||
return bytesToString(bytes)
|
||||
|
||||
def decrypt(self, ciphertext):
|
||||
TripleDES.decrypt(self, ciphertext)
|
||||
bytes = stringToBytes(ciphertext)
|
||||
cryptlib_py.cryptDecrypt(self.context, bytes)
|
||||
return bytesToString(bytes)
|
||||
@@ -1,49 +0,0 @@
|
||||
"""OpenSSL/M2Crypto AES implementation."""
|
||||
|
||||
from .cryptomath import *
|
||||
from .AES import *
|
||||
|
||||
if m2cryptoLoaded:
|
||||
|
||||
def new(key, mode, IV):
|
||||
return OpenSSL_AES(key, mode, IV)
|
||||
|
||||
class OpenSSL_AES(AES):
|
||||
|
||||
def __init__(self, key, mode, IV):
|
||||
AES.__init__(self, key, mode, IV, "openssl")
|
||||
self.key = key
|
||||
self.IV = IV
|
||||
|
||||
def _createContext(self, encrypt):
|
||||
context = m2.cipher_ctx_new()
|
||||
if len(self.key)==16:
|
||||
cipherType = m2.aes_128_cbc()
|
||||
if len(self.key)==24:
|
||||
cipherType = m2.aes_192_cbc()
|
||||
if len(self.key)==32:
|
||||
cipherType = m2.aes_256_cbc()
|
||||
m2.cipher_init(context, cipherType, self.key, self.IV, encrypt)
|
||||
return context
|
||||
|
||||
def encrypt(self, plaintext):
|
||||
AES.encrypt(self, plaintext)
|
||||
context = self._createContext(1)
|
||||
ciphertext = m2.cipher_update(context, plaintext)
|
||||
m2.cipher_ctx_free(context)
|
||||
self.IV = ciphertext[-self.block_size:]
|
||||
return ciphertext
|
||||
|
||||
def decrypt(self, ciphertext):
|
||||
AES.decrypt(self, ciphertext)
|
||||
context = self._createContext(0)
|
||||
#I think M2Crypto has a bug - it fails to decrypt and return the last block passed in.
|
||||
#To work around this, we append sixteen zeros to the string, below:
|
||||
plaintext = m2.cipher_update(context, ciphertext+('\0'*16))
|
||||
|
||||
#If this bug is ever fixed, then plaintext will end up having a garbage
|
||||
#plaintext block on the end. That's okay - the below code will discard it.
|
||||
plaintext = plaintext[:len(ciphertext)]
|
||||
m2.cipher_ctx_free(context)
|
||||
self.IV = ciphertext[-self.block_size:]
|
||||
return plaintext
|
||||
@@ -1,25 +0,0 @@
|
||||
"""OpenSSL/M2Crypto RC4 implementation."""
|
||||
|
||||
from .cryptomath import *
|
||||
from .RC4 import RC4
|
||||
|
||||
if m2cryptoLoaded:
|
||||
|
||||
def new(key):
|
||||
return OpenSSL_RC4(key)
|
||||
|
||||
class OpenSSL_RC4(RC4):
|
||||
|
||||
def __init__(self, key):
|
||||
RC4.__init__(self, key, "openssl")
|
||||
self.rc4 = m2.rc4_new()
|
||||
m2.rc4_set_key(self.rc4, key)
|
||||
|
||||
def __del__(self):
|
||||
m2.rc4_free(self.rc4)
|
||||
|
||||
def encrypt(self, plaintext):
|
||||
return m2.rc4_update(self.rc4, plaintext)
|
||||
|
||||
def decrypt(self, ciphertext):
|
||||
return self.encrypt(ciphertext)
|
||||
@@ -1,148 +0,0 @@
|
||||
"""OpenSSL/M2Crypto RSA implementation."""
|
||||
|
||||
from .cryptomath import *
|
||||
|
||||
from .RSAKey import *
|
||||
from .Python_RSAKey import Python_RSAKey
|
||||
|
||||
#copied from M2Crypto.util.py, so when we load the local copy of m2
|
||||
#we can still use it
|
||||
def password_callback(v, prompt1='Enter private key passphrase:',
|
||||
prompt2='Verify passphrase:'):
|
||||
from getpass import getpass
|
||||
while 1:
|
||||
try:
|
||||
p1=getpass(prompt1)
|
||||
if v:
|
||||
p2=getpass(prompt2)
|
||||
if p1==p2:
|
||||
break
|
||||
else:
|
||||
break
|
||||
except KeyboardInterrupt:
|
||||
return None
|
||||
return p1
|
||||
|
||||
|
||||
if m2cryptoLoaded:
|
||||
class OpenSSL_RSAKey(RSAKey):
|
||||
def __init__(self, n=0, e=0):
|
||||
self.rsa = None
|
||||
self._hasPrivateKey = False
|
||||
if (n and not e) or (e and not n):
|
||||
raise AssertionError()
|
||||
if n and e:
|
||||
self.rsa = m2.rsa_new()
|
||||
m2.rsa_set_n(self.rsa, numberToMPI(n))
|
||||
m2.rsa_set_e(self.rsa, numberToMPI(e))
|
||||
|
||||
def __del__(self):
|
||||
if self.rsa:
|
||||
m2.rsa_free(self.rsa)
|
||||
|
||||
def __getattr__(self, name):
|
||||
if name == 'e':
|
||||
if not self.rsa:
|
||||
return 0
|
||||
return mpiToNumber(m2.rsa_get_e(self.rsa))
|
||||
elif name == 'n':
|
||||
if not self.rsa:
|
||||
return 0
|
||||
return mpiToNumber(m2.rsa_get_n(self.rsa))
|
||||
else:
|
||||
raise AttributeError
|
||||
|
||||
def hasPrivateKey(self):
|
||||
return self._hasPrivateKey
|
||||
|
||||
def hash(self):
|
||||
return Python_RSAKey(self.n, self.e).hash()
|
||||
|
||||
def _rawPrivateKeyOp(self, m):
|
||||
s = numberToString(m)
|
||||
byteLength = numBytes(self.n)
|
||||
if len(s)== byteLength:
|
||||
pass
|
||||
elif len(s) == byteLength-1:
|
||||
s = '\0' + s
|
||||
else:
|
||||
raise AssertionError()
|
||||
c = stringToNumber(m2.rsa_private_encrypt(self.rsa, s,
|
||||
m2.no_padding))
|
||||
return c
|
||||
|
||||
def _rawPublicKeyOp(self, c):
|
||||
s = numberToString(c)
|
||||
byteLength = numBytes(self.n)
|
||||
if len(s)== byteLength:
|
||||
pass
|
||||
elif len(s) == byteLength-1:
|
||||
s = '\0' + s
|
||||
else:
|
||||
raise AssertionError()
|
||||
m = stringToNumber(m2.rsa_public_decrypt(self.rsa, s,
|
||||
m2.no_padding))
|
||||
return m
|
||||
|
||||
def acceptsPassword(self): return True
|
||||
|
||||
def write(self, password=None):
|
||||
bio = m2.bio_new(m2.bio_s_mem())
|
||||
if self._hasPrivateKey:
|
||||
if password:
|
||||
def f(v): return password
|
||||
m2.rsa_write_key(self.rsa, bio, m2.des_ede_cbc(), f)
|
||||
else:
|
||||
def f(): pass
|
||||
m2.rsa_write_key_no_cipher(self.rsa, bio, f)
|
||||
else:
|
||||
if password:
|
||||
raise AssertionError()
|
||||
m2.rsa_write_pub_key(self.rsa, bio)
|
||||
s = m2.bio_read(bio, m2.bio_ctrl_pending(bio))
|
||||
m2.bio_free(bio)
|
||||
return s
|
||||
|
||||
def writeXMLPublicKey(self, indent=''):
|
||||
return Python_RSAKey(self.n, self.e).write(indent)
|
||||
|
||||
def generate(bits):
|
||||
key = OpenSSL_RSAKey()
|
||||
def f():pass
|
||||
key.rsa = m2.rsa_generate_key(bits, 3, f)
|
||||
key._hasPrivateKey = True
|
||||
return key
|
||||
generate = staticmethod(generate)
|
||||
|
||||
def parse(s, passwordCallback=None):
|
||||
if s.startswith("-----BEGIN "):
|
||||
if passwordCallback==None:
|
||||
callback = password_callback
|
||||
else:
|
||||
def f(v, prompt1=None, prompt2=None):
|
||||
return passwordCallback()
|
||||
callback = f
|
||||
bio = m2.bio_new(m2.bio_s_mem())
|
||||
try:
|
||||
m2.bio_write(bio, s)
|
||||
key = OpenSSL_RSAKey()
|
||||
if s.startswith("-----BEGIN RSA PRIVATE KEY-----"):
|
||||
def f():pass
|
||||
key.rsa = m2.rsa_read_key(bio, callback)
|
||||
if key.rsa == None:
|
||||
raise SyntaxError()
|
||||
key._hasPrivateKey = True
|
||||
elif s.startswith("-----BEGIN PUBLIC KEY-----"):
|
||||
key.rsa = m2.rsa_read_pub_key(bio)
|
||||
if key.rsa == None:
|
||||
raise SyntaxError()
|
||||
key._hasPrivateKey = False
|
||||
else:
|
||||
raise SyntaxError()
|
||||
return key
|
||||
finally:
|
||||
m2.bio_free(bio)
|
||||
else:
|
||||
raise SyntaxError()
|
||||
|
||||
parse = staticmethod(parse)
|
||||
@@ -1,44 +0,0 @@
|
||||
"""OpenSSL/M2Crypto 3DES implementation."""
|
||||
|
||||
from .cryptomath import *
|
||||
from .TripleDES import *
|
||||
|
||||
if m2cryptoLoaded:
|
||||
|
||||
def new(key, mode, IV):
|
||||
return OpenSSL_TripleDES(key, mode, IV)
|
||||
|
||||
class OpenSSL_TripleDES(TripleDES):
|
||||
|
||||
def __init__(self, key, mode, IV):
|
||||
TripleDES.__init__(self, key, mode, IV, "openssl")
|
||||
self.key = key
|
||||
self.IV = IV
|
||||
|
||||
def _createContext(self, encrypt):
|
||||
context = m2.cipher_ctx_new()
|
||||
cipherType = m2.des_ede3_cbc()
|
||||
m2.cipher_init(context, cipherType, self.key, self.IV, encrypt)
|
||||
return context
|
||||
|
||||
def encrypt(self, plaintext):
|
||||
TripleDES.encrypt(self, plaintext)
|
||||
context = self._createContext(1)
|
||||
ciphertext = m2.cipher_update(context, plaintext)
|
||||
m2.cipher_ctx_free(context)
|
||||
self.IV = ciphertext[-self.block_size:]
|
||||
return ciphertext
|
||||
|
||||
def decrypt(self, ciphertext):
|
||||
TripleDES.decrypt(self, ciphertext)
|
||||
context = self._createContext(0)
|
||||
#I think M2Crypto has a bug - it fails to decrypt and return the last block passed in.
|
||||
#To work around this, we append sixteen zeros to the string, below:
|
||||
plaintext = m2.cipher_update(context, ciphertext+('\0'*16))
|
||||
|
||||
#If this bug is ever fixed, then plaintext will end up having a garbage
|
||||
#plaintext block on the end. That's okay - the below code will ignore it.
|
||||
plaintext = plaintext[:len(ciphertext)]
|
||||
m2.cipher_ctx_free(context)
|
||||
self.IV = ciphertext[-self.block_size:]
|
||||
return plaintext
|
||||
@@ -1,22 +0,0 @@
|
||||
"""PyCrypto AES implementation."""
|
||||
|
||||
from .cryptomath import *
|
||||
from .AES import *
|
||||
|
||||
if pycryptoLoaded:
|
||||
import Crypto.Cipher.AES
|
||||
|
||||
def new(key, mode, IV):
|
||||
return PyCrypto_AES(key, mode, IV)
|
||||
|
||||
class PyCrypto_AES(AES):
|
||||
|
||||
def __init__(self, key, mode, IV):
|
||||
AES.__init__(self, key, mode, IV, "pycrypto")
|
||||
self.context = Crypto.Cipher.AES.new(key, mode, IV)
|
||||
|
||||
def encrypt(self, plaintext):
|
||||
return self.context.encrypt(plaintext)
|
||||
|
||||
def decrypt(self, ciphertext):
|
||||
return self.context.decrypt(ciphertext)
|
||||
@@ -1,22 +0,0 @@
|
||||
"""PyCrypto RC4 implementation."""
|
||||
|
||||
from .cryptomath import *
|
||||
from .RC4 import *
|
||||
|
||||
if pycryptoLoaded:
|
||||
import Crypto.Cipher.ARC4
|
||||
|
||||
def new(key):
|
||||
return PyCrypto_RC4(key)
|
||||
|
||||
class PyCrypto_RC4(RC4):
|
||||
|
||||
def __init__(self, key):
|
||||
RC4.__init__(self, key, "pycrypto")
|
||||
self.context = Crypto.Cipher.ARC4.new(key)
|
||||
|
||||
def encrypt(self, plaintext):
|
||||
return self.context.encrypt(plaintext)
|
||||
|
||||
def decrypt(self, ciphertext):
|
||||
return self.context.decrypt(ciphertext)
|
||||
@@ -1,61 +0,0 @@
|
||||
"""PyCrypto RSA implementation."""
|
||||
|
||||
from .cryptomath import *
|
||||
|
||||
from .RSAKey import *
|
||||
from .Python_RSAKey import Python_RSAKey
|
||||
|
||||
if pycryptoLoaded:
|
||||
|
||||
from Crypto.PublicKey import RSA
|
||||
|
||||
class PyCrypto_RSAKey(RSAKey):
|
||||
def __init__(self, n=0, e=0, d=0, p=0, q=0, dP=0, dQ=0, qInv=0):
|
||||
if not d:
|
||||
self.rsa = RSA.construct( (n, e) )
|
||||
else:
|
||||
self.rsa = RSA.construct( (n, e, d, p, q) )
|
||||
|
||||
def __getattr__(self, name):
|
||||
return getattr(self.rsa, name)
|
||||
|
||||
def hasPrivateKey(self):
|
||||
return self.rsa.has_private()
|
||||
|
||||
def hash(self):
|
||||
return Python_RSAKey(self.n, self.e).hash()
|
||||
|
||||
def _rawPrivateKeyOp(self, m):
|
||||
s = numberToString(m)
|
||||
byteLength = numBytes(self.n)
|
||||
if len(s)== byteLength:
|
||||
pass
|
||||
elif len(s) == byteLength-1:
|
||||
s = '\0' + s
|
||||
else:
|
||||
raise AssertionError()
|
||||
c = stringToNumber(self.rsa.decrypt((s,)))
|
||||
return c
|
||||
|
||||
def _rawPublicKeyOp(self, c):
|
||||
s = numberToString(c)
|
||||
byteLength = numBytes(self.n)
|
||||
if len(s)== byteLength:
|
||||
pass
|
||||
elif len(s) == byteLength-1:
|
||||
s = '\0' + s
|
||||
else:
|
||||
raise AssertionError()
|
||||
m = stringToNumber(self.rsa.encrypt(s, None)[0])
|
||||
return m
|
||||
|
||||
def writeXMLPublicKey(self, indent=''):
|
||||
return Python_RSAKey(self.n, self.e).write(indent)
|
||||
|
||||
def generate(bits):
|
||||
key = PyCrypto_RSAKey()
|
||||
def f(numBytes):
|
||||
return bytesToString(getRandomBytes(numBytes))
|
||||
key.rsa = RSA.generate(bits, f)
|
||||
return key
|
||||
generate = staticmethod(generate)
|
||||
@@ -1,22 +0,0 @@
|
||||
"""PyCrypto 3DES implementation."""
|
||||
|
||||
from .cryptomath import *
|
||||
from .TripleDES import *
|
||||
|
||||
if pycryptoLoaded:
|
||||
import Crypto.Cipher.DES3
|
||||
|
||||
def new(key, mode, IV):
|
||||
return PyCrypto_TripleDES(key, mode, IV)
|
||||
|
||||
class PyCrypto_TripleDES(TripleDES):
|
||||
|
||||
def __init__(self, key, mode, IV):
|
||||
TripleDES.__init__(self, key, mode, IV, "pycrypto")
|
||||
self.context = Crypto.Cipher.DES3.new(key, mode, IV)
|
||||
|
||||
def encrypt(self, plaintext):
|
||||
return self.context.encrypt(plaintext)
|
||||
|
||||
def decrypt(self, ciphertext):
|
||||
return self.context.decrypt(ciphertext)
|
||||
@@ -1,68 +0,0 @@
|
||||
"""Pure-Python AES implementation."""
|
||||
|
||||
from .cryptomath import *
|
||||
|
||||
from .AES import *
|
||||
from .rijndael import rijndael
|
||||
|
||||
def new(key, mode, IV):
|
||||
return Python_AES(key, mode, IV)
|
||||
|
||||
class Python_AES(AES):
|
||||
def __init__(self, key, mode, IV):
|
||||
AES.__init__(self, key, mode, IV, "python")
|
||||
self.rijndael = rijndael(key, 16)
|
||||
self.IV = IV
|
||||
|
||||
def encrypt(self, plaintext):
|
||||
AES.encrypt(self, plaintext)
|
||||
|
||||
plaintextBytes = stringToBytes(plaintext)
|
||||
chainBytes = stringToBytes(self.IV)
|
||||
|
||||
#CBC Mode: For each block...
|
||||
for x in range(len(plaintextBytes)/16):
|
||||
|
||||
#XOR with the chaining block
|
||||
blockBytes = plaintextBytes[x*16 : (x*16)+16]
|
||||
for y in range(16):
|
||||
blockBytes[y] ^= chainBytes[y]
|
||||
blockString = bytesToString(blockBytes)
|
||||
|
||||
#Encrypt it
|
||||
encryptedBytes = stringToBytes(self.rijndael.encrypt(blockString))
|
||||
|
||||
#Overwrite the input with the output
|
||||
for y in range(16):
|
||||
plaintextBytes[(x*16)+y] = encryptedBytes[y]
|
||||
|
||||
#Set the next chaining block
|
||||
chainBytes = encryptedBytes
|
||||
|
||||
self.IV = bytesToString(chainBytes)
|
||||
return bytesToString(plaintextBytes)
|
||||
|
||||
def decrypt(self, ciphertext):
|
||||
AES.decrypt(self, ciphertext)
|
||||
|
||||
ciphertextBytes = stringToBytes(ciphertext)
|
||||
chainBytes = stringToBytes(self.IV)
|
||||
|
||||
#CBC Mode: For each block...
|
||||
for x in range(len(ciphertextBytes)/16):
|
||||
|
||||
#Decrypt it
|
||||
blockBytes = ciphertextBytes[x*16 : (x*16)+16]
|
||||
blockString = bytesToString(blockBytes)
|
||||
decryptedBytes = stringToBytes(self.rijndael.decrypt(blockString))
|
||||
|
||||
#XOR with the chaining block and overwrite the input with output
|
||||
for y in range(16):
|
||||
decryptedBytes[y] ^= chainBytes[y]
|
||||
ciphertextBytes[(x*16)+y] = decryptedBytes[y]
|
||||
|
||||
#Set the next chaining block
|
||||
chainBytes = blockBytes
|
||||
|
||||
self.IV = bytesToString(chainBytes)
|
||||
return bytesToString(ciphertextBytes)
|
||||
@@ -1,39 +0,0 @@
|
||||
"""Pure-Python RC4 implementation."""
|
||||
|
||||
from .RC4 import RC4
|
||||
from .cryptomath import *
|
||||
|
||||
def new(key):
|
||||
return Python_RC4(key)
|
||||
|
||||
class Python_RC4(RC4):
|
||||
def __init__(self, key):
|
||||
RC4.__init__(self, key, "python")
|
||||
keyBytes = stringToBytes(key)
|
||||
S = [i for i in range(256)]
|
||||
j = 0
|
||||
for i in range(256):
|
||||
j = (j + S[i] + keyBytes[i % len(keyBytes)]) % 256
|
||||
S[i], S[j] = S[j], S[i]
|
||||
|
||||
self.S = S
|
||||
self.i = 0
|
||||
self.j = 0
|
||||
|
||||
def encrypt(self, plaintext):
|
||||
plaintextBytes = stringToBytes(plaintext)
|
||||
S = self.S
|
||||
i = self.i
|
||||
j = self.j
|
||||
for x in range(len(plaintextBytes)):
|
||||
i = (i + 1) % 256
|
||||
j = (j + S[i]) % 256
|
||||
S[i], S[j] = S[j], S[i]
|
||||
t = (S[i] + S[j]) % 256
|
||||
plaintextBytes[x] ^= S[t]
|
||||
self.i = i
|
||||
self.j = j
|
||||
return bytesToString(plaintextBytes)
|
||||
|
||||
def decrypt(self, ciphertext):
|
||||
return self.encrypt(ciphertext)
|
||||
@@ -1,209 +0,0 @@
|
||||
"""Pure-Python RSA implementation."""
|
||||
|
||||
from .cryptomath import *
|
||||
from . import xmltools
|
||||
from .ASN1Parser import ASN1Parser
|
||||
from .RSAKey import *
|
||||
|
||||
class Python_RSAKey(RSAKey):
|
||||
def __init__(self, n=0, e=0, d=0, p=0, q=0, dP=0, dQ=0, qInv=0):
|
||||
if (n and not e) or (e and not n):
|
||||
raise AssertionError()
|
||||
self.n = n
|
||||
self.e = e
|
||||
self.d = d
|
||||
self.p = p
|
||||
self.q = q
|
||||
self.dP = dP
|
||||
self.dQ = dQ
|
||||
self.qInv = qInv
|
||||
self.blinder = 0
|
||||
self.unblinder = 0
|
||||
|
||||
def hasPrivateKey(self):
|
||||
return self.d != 0
|
||||
|
||||
def hash(self):
|
||||
s = self.writeXMLPublicKey('\t\t')
|
||||
return hashAndBase64(s.strip())
|
||||
|
||||
def _rawPrivateKeyOp(self, m):
|
||||
#Create blinding values, on the first pass:
|
||||
if not self.blinder:
|
||||
self.unblinder = getRandomNumber(2, self.n)
|
||||
self.blinder = powMod(invMod(self.unblinder, self.n), self.e,
|
||||
self.n)
|
||||
|
||||
#Blind the input
|
||||
m = (m * self.blinder) % self.n
|
||||
|
||||
#Perform the RSA operation
|
||||
c = self._rawPrivateKeyOpHelper(m)
|
||||
|
||||
#Unblind the output
|
||||
c = (c * self.unblinder) % self.n
|
||||
|
||||
#Update blinding values
|
||||
self.blinder = (self.blinder * self.blinder) % self.n
|
||||
self.unblinder = (self.unblinder * self.unblinder) % self.n
|
||||
|
||||
#Return the output
|
||||
return c
|
||||
|
||||
|
||||
def _rawPrivateKeyOpHelper(self, m):
|
||||
#Non-CRT version
|
||||
#c = powMod(m, self.d, self.n)
|
||||
|
||||
#CRT version (~3x faster)
|
||||
s1 = powMod(m, self.dP, self.p)
|
||||
s2 = powMod(m, self.dQ, self.q)
|
||||
h = ((s1 - s2) * self.qInv) % self.p
|
||||
c = s2 + self.q * h
|
||||
return c
|
||||
|
||||
def _rawPublicKeyOp(self, c):
|
||||
m = powMod(c, self.e, self.n)
|
||||
return m
|
||||
|
||||
def acceptsPassword(self): return False
|
||||
|
||||
def write(self, indent=''):
|
||||
if self.d:
|
||||
s = indent+'<privateKey xmlns="http://trevp.net/rsa">\n'
|
||||
else:
|
||||
s = indent+'<publicKey xmlns="http://trevp.net/rsa">\n'
|
||||
s += indent+'\t<n>%s</n>\n' % numberToBase64(self.n)
|
||||
s += indent+'\t<e>%s</e>\n' % numberToBase64(self.e)
|
||||
if self.d:
|
||||
s += indent+'\t<d>%s</d>\n' % numberToBase64(self.d)
|
||||
s += indent+'\t<p>%s</p>\n' % numberToBase64(self.p)
|
||||
s += indent+'\t<q>%s</q>\n' % numberToBase64(self.q)
|
||||
s += indent+'\t<dP>%s</dP>\n' % numberToBase64(self.dP)
|
||||
s += indent+'\t<dQ>%s</dQ>\n' % numberToBase64(self.dQ)
|
||||
s += indent+'\t<qInv>%s</qInv>\n' % numberToBase64(self.qInv)
|
||||
s += indent+'</privateKey>'
|
||||
else:
|
||||
s += indent+'</publicKey>'
|
||||
#Only add \n if part of a larger structure
|
||||
if indent != '':
|
||||
s += '\n'
|
||||
return s
|
||||
|
||||
def writeXMLPublicKey(self, indent=''):
|
||||
return Python_RSAKey(self.n, self.e).write(indent)
|
||||
|
||||
def generate(bits):
|
||||
key = Python_RSAKey()
|
||||
p = getRandomPrime(bits/2, False)
|
||||
q = getRandomPrime(bits/2, False)
|
||||
t = lcm(p-1, q-1)
|
||||
key.n = p * q
|
||||
key.e = 3 #Needed to be long, for Java
|
||||
key.d = invMod(key.e, t)
|
||||
key.p = p
|
||||
key.q = q
|
||||
key.dP = key.d % (p-1)
|
||||
key.dQ = key.d % (q-1)
|
||||
key.qInv = invMod(q, p)
|
||||
return key
|
||||
generate = staticmethod(generate)
|
||||
|
||||
def parsePEM(s, passwordCallback=None):
|
||||
"""Parse a string containing a <privateKey> or <publicKey>, or
|
||||
PEM-encoded key."""
|
||||
|
||||
start = s.find("-----BEGIN PRIVATE KEY-----")
|
||||
if start != -1:
|
||||
end = s.find("-----END PRIVATE KEY-----")
|
||||
if end == -1:
|
||||
raise SyntaxError("Missing PEM Postfix")
|
||||
s = s[start+len("-----BEGIN PRIVATE KEY -----") : end]
|
||||
bytes = base64ToBytes(s)
|
||||
return Python_RSAKey._parsePKCS8(bytes)
|
||||
else:
|
||||
start = s.find("-----BEGIN RSA PRIVATE KEY-----")
|
||||
if start != -1:
|
||||
end = s.find("-----END RSA PRIVATE KEY-----")
|
||||
if end == -1:
|
||||
raise SyntaxError("Missing PEM Postfix")
|
||||
s = s[start+len("-----BEGIN RSA PRIVATE KEY -----") : end]
|
||||
bytes = base64ToBytes(s)
|
||||
return Python_RSAKey._parseSSLeay(bytes)
|
||||
raise SyntaxError("Missing PEM Prefix")
|
||||
parsePEM = staticmethod(parsePEM)
|
||||
|
||||
def parseXML(s):
|
||||
element = xmltools.parseAndStripWhitespace(s)
|
||||
return Python_RSAKey._parseXML(element)
|
||||
parseXML = staticmethod(parseXML)
|
||||
|
||||
def _parsePKCS8(bytes):
|
||||
p = ASN1Parser(bytes)
|
||||
|
||||
version = p.getChild(0).value[0]
|
||||
if version != 0:
|
||||
raise SyntaxError("Unrecognized PKCS8 version")
|
||||
|
||||
rsaOID = p.getChild(1).value
|
||||
if list(rsaOID) != [6, 9, 42, 134, 72, 134, 247, 13, 1, 1, 1, 5, 0]:
|
||||
raise SyntaxError("Unrecognized AlgorithmIdentifier")
|
||||
|
||||
#Get the privateKey
|
||||
privateKeyP = p.getChild(2)
|
||||
|
||||
#Adjust for OCTET STRING encapsulation
|
||||
privateKeyP = ASN1Parser(privateKeyP.value)
|
||||
|
||||
return Python_RSAKey._parseASN1PrivateKey(privateKeyP)
|
||||
_parsePKCS8 = staticmethod(_parsePKCS8)
|
||||
|
||||
def _parseSSLeay(bytes):
|
||||
privateKeyP = ASN1Parser(bytes)
|
||||
return Python_RSAKey._parseASN1PrivateKey(privateKeyP)
|
||||
_parseSSLeay = staticmethod(_parseSSLeay)
|
||||
|
||||
def _parseASN1PrivateKey(privateKeyP):
|
||||
version = privateKeyP.getChild(0).value[0]
|
||||
if version != 0:
|
||||
raise SyntaxError("Unrecognized RSAPrivateKey version")
|
||||
n = bytesToNumber(privateKeyP.getChild(1).value)
|
||||
e = bytesToNumber(privateKeyP.getChild(2).value)
|
||||
d = bytesToNumber(privateKeyP.getChild(3).value)
|
||||
p = bytesToNumber(privateKeyP.getChild(4).value)
|
||||
q = bytesToNumber(privateKeyP.getChild(5).value)
|
||||
dP = bytesToNumber(privateKeyP.getChild(6).value)
|
||||
dQ = bytesToNumber(privateKeyP.getChild(7).value)
|
||||
qInv = bytesToNumber(privateKeyP.getChild(8).value)
|
||||
return Python_RSAKey(n, e, d, p, q, dP, dQ, qInv)
|
||||
_parseASN1PrivateKey = staticmethod(_parseASN1PrivateKey)
|
||||
|
||||
def _parseXML(element):
|
||||
try:
|
||||
xmltools.checkName(element, "privateKey")
|
||||
except SyntaxError:
|
||||
xmltools.checkName(element, "publicKey")
|
||||
|
||||
#Parse attributes
|
||||
xmltools.getReqAttribute(element, "xmlns", "http://trevp.net/rsa\Z")
|
||||
xmltools.checkNoMoreAttributes(element)
|
||||
|
||||
#Parse public values (<n> and <e>)
|
||||
n = base64ToNumber(xmltools.getText(xmltools.getChild(element, 0, "n"), xmltools.base64RegEx))
|
||||
e = base64ToNumber(xmltools.getText(xmltools.getChild(element, 1, "e"), xmltools.base64RegEx))
|
||||
d = 0
|
||||
p = 0
|
||||
q = 0
|
||||
dP = 0
|
||||
dQ = 0
|
||||
qInv = 0
|
||||
#Parse private values, if present
|
||||
if element.childNodes.length>=3:
|
||||
d = base64ToNumber(xmltools.getText(xmltools.getChild(element, 2, "d"), xmltools.base64RegEx))
|
||||
p = base64ToNumber(xmltools.getText(xmltools.getChild(element, 3, "p"), xmltools.base64RegEx))
|
||||
q = base64ToNumber(xmltools.getText(xmltools.getChild(element, 4, "q"), xmltools.base64RegEx))
|
||||
dP = base64ToNumber(xmltools.getText(xmltools.getChild(element, 5, "dP"), xmltools.base64RegEx))
|
||||
dQ = base64ToNumber(xmltools.getText(xmltools.getChild(element, 6, "dQ"), xmltools.base64RegEx))
|
||||
qInv = base64ToNumber(xmltools.getText(xmltools.getLastChild(element, 7, "qInv"), xmltools.base64RegEx))
|
||||
return Python_RSAKey(n, e, d, p, q, dP, dQ, qInv)
|
||||
_parseXML = staticmethod(_parseXML)
|
||||
@@ -1,17 +0,0 @@
|
||||
"""Abstract class for RC4."""
|
||||
|
||||
from .compat import * #For False
|
||||
|
||||
class RC4:
|
||||
def __init__(self, keyBytes, implementation):
|
||||
if len(keyBytes) < 16 or len(keyBytes) > 256:
|
||||
raise ValueError()
|
||||
self.isBlockCipher = False
|
||||
self.name = "rc4"
|
||||
self.implementation = implementation
|
||||
|
||||
def encrypt(self, plaintext):
|
||||
raise NotImplementedError()
|
||||
|
||||
def decrypt(self, ciphertext):
|
||||
raise NotImplementedError()
|
||||
@@ -1,264 +0,0 @@
|
||||
"""Abstract class for RSA."""
|
||||
|
||||
from .cryptomath import *
|
||||
|
||||
|
||||
class RSAKey:
|
||||
"""This is an abstract base class for RSA keys.
|
||||
|
||||
Particular implementations of RSA keys, such as
|
||||
L{OpenSSL_RSAKey.OpenSSL_RSAKey},
|
||||
L{Python_RSAKey.Python_RSAKey}, and
|
||||
L{PyCrypto_RSAKey.PyCrypto_RSAKey},
|
||||
inherit from this.
|
||||
|
||||
To create or parse an RSA key, don't use one of these classes
|
||||
directly. Instead, use the factory functions in
|
||||
L{tlslite.utils.keyfactory}.
|
||||
"""
|
||||
|
||||
def __init__(self, n=0, e=0):
|
||||
"""Create a new RSA key.
|
||||
|
||||
If n and e are passed in, the new key will be initialized.
|
||||
|
||||
@type n: int
|
||||
@param n: RSA modulus.
|
||||
|
||||
@type e: int
|
||||
@param e: RSA public exponent.
|
||||
"""
|
||||
raise NotImplementedError()
|
||||
|
||||
def __len__(self):
|
||||
"""Return the length of this key in bits.
|
||||
|
||||
@rtype: int
|
||||
"""
|
||||
return numBits(self.n)
|
||||
|
||||
def hasPrivateKey(self):
|
||||
"""Return whether or not this key has a private component.
|
||||
|
||||
@rtype: bool
|
||||
"""
|
||||
raise NotImplementedError()
|
||||
|
||||
def hash(self):
|
||||
"""Return the cryptoID <keyHash> value corresponding to this
|
||||
key.
|
||||
|
||||
@rtype: str
|
||||
"""
|
||||
raise NotImplementedError()
|
||||
|
||||
def getSigningAlgorithm(self):
|
||||
"""Return the cryptoID sigAlgo value corresponding to this key.
|
||||
|
||||
@rtype: str
|
||||
"""
|
||||
return "pkcs1-sha1"
|
||||
|
||||
def hashAndSign(self, bytes):
|
||||
"""Hash and sign the passed-in bytes.
|
||||
|
||||
This requires the key to have a private component. It performs
|
||||
a PKCS1-SHA1 signature on the passed-in data.
|
||||
|
||||
@type bytes: str or L{array.array} of unsigned bytes
|
||||
@param bytes: The value which will be hashed and signed.
|
||||
|
||||
@rtype: L{array.array} of unsigned bytes.
|
||||
@return: A PKCS1-SHA1 signature on the passed-in data.
|
||||
"""
|
||||
if not isinstance(bytes, type("")):
|
||||
bytes = bytesToString(bytes)
|
||||
hashBytes = stringToBytes(sha1(bytes).digest())
|
||||
prefixedHashBytes = self._addPKCS1SHA1Prefix(hashBytes)
|
||||
sigBytes = self.sign(prefixedHashBytes)
|
||||
return sigBytes
|
||||
|
||||
def hashAndVerify(self, sigBytes, bytes):
|
||||
"""Hash and verify the passed-in bytes with the signature.
|
||||
|
||||
This verifies a PKCS1-SHA1 signature on the passed-in data.
|
||||
|
||||
@type sigBytes: L{array.array} of unsigned bytes
|
||||
@param sigBytes: A PKCS1-SHA1 signature.
|
||||
|
||||
@type bytes: str or L{array.array} of unsigned bytes
|
||||
@param bytes: The value which will be hashed and verified.
|
||||
|
||||
@rtype: bool
|
||||
@return: Whether the signature matches the passed-in data.
|
||||
"""
|
||||
if not isinstance(bytes, type("")):
|
||||
bytes = bytesToString(bytes)
|
||||
hashBytes = stringToBytes(sha1(bytes).digest())
|
||||
prefixedHashBytes = self._addPKCS1SHA1Prefix(hashBytes)
|
||||
return self.verify(sigBytes, prefixedHashBytes)
|
||||
|
||||
def sign(self, bytes):
|
||||
"""Sign the passed-in bytes.
|
||||
|
||||
This requires the key to have a private component. It performs
|
||||
a PKCS1 signature on the passed-in data.
|
||||
|
||||
@type bytes: L{array.array} of unsigned bytes
|
||||
@param bytes: The value which will be signed.
|
||||
|
||||
@rtype: L{array.array} of unsigned bytes.
|
||||
@return: A PKCS1 signature on the passed-in data.
|
||||
"""
|
||||
if not self.hasPrivateKey():
|
||||
raise AssertionError()
|
||||
paddedBytes = self._addPKCS1Padding(bytes, 1)
|
||||
m = bytesToNumber(paddedBytes)
|
||||
if m >= self.n:
|
||||
raise ValueError()
|
||||
c = self._rawPrivateKeyOp(m)
|
||||
sigBytes = numberToBytes(c)
|
||||
return sigBytes
|
||||
|
||||
def verify(self, sigBytes, bytes):
|
||||
"""Verify the passed-in bytes with the signature.
|
||||
|
||||
This verifies a PKCS1 signature on the passed-in data.
|
||||
|
||||
@type sigBytes: L{array.array} of unsigned bytes
|
||||
@param sigBytes: A PKCS1 signature.
|
||||
|
||||
@type bytes: L{array.array} of unsigned bytes
|
||||
@param bytes: The value which will be verified.
|
||||
|
||||
@rtype: bool
|
||||
@return: Whether the signature matches the passed-in data.
|
||||
"""
|
||||
paddedBytes = self._addPKCS1Padding(bytes, 1)
|
||||
c = bytesToNumber(sigBytes)
|
||||
if c >= self.n:
|
||||
return False
|
||||
m = self._rawPublicKeyOp(c)
|
||||
checkBytes = numberToBytes(m)
|
||||
return checkBytes == paddedBytes
|
||||
|
||||
def encrypt(self, bytes):
|
||||
"""Encrypt the passed-in bytes.
|
||||
|
||||
This performs PKCS1 encryption of the passed-in data.
|
||||
|
||||
@type bytes: L{array.array} of unsigned bytes
|
||||
@param bytes: The value which will be encrypted.
|
||||
|
||||
@rtype: L{array.array} of unsigned bytes.
|
||||
@return: A PKCS1 encryption of the passed-in data.
|
||||
"""
|
||||
paddedBytes = self._addPKCS1Padding(bytes, 2)
|
||||
m = bytesToNumber(paddedBytes)
|
||||
if m >= self.n:
|
||||
raise ValueError()
|
||||
c = self._rawPublicKeyOp(m)
|
||||
encBytes = numberToBytes(c)
|
||||
return encBytes
|
||||
|
||||
def decrypt(self, encBytes):
|
||||
"""Decrypt the passed-in bytes.
|
||||
|
||||
This requires the key to have a private component. It performs
|
||||
PKCS1 decryption of the passed-in data.
|
||||
|
||||
@type encBytes: L{array.array} of unsigned bytes
|
||||
@param encBytes: The value which will be decrypted.
|
||||
|
||||
@rtype: L{array.array} of unsigned bytes or None.
|
||||
@return: A PKCS1 decryption of the passed-in data or None if
|
||||
the data is not properly formatted.
|
||||
"""
|
||||
if not self.hasPrivateKey():
|
||||
raise AssertionError()
|
||||
c = bytesToNumber(encBytes)
|
||||
if c >= self.n:
|
||||
return None
|
||||
m = self._rawPrivateKeyOp(c)
|
||||
decBytes = numberToBytes(m)
|
||||
if (len(decBytes) != numBytes(self.n)-1): #Check first byte
|
||||
return None
|
||||
if decBytes[0] != 2: #Check second byte
|
||||
return None
|
||||
for x in range(len(decBytes)-1): #Scan through for zero separator
|
||||
if decBytes[x]== 0:
|
||||
break
|
||||
else:
|
||||
return None
|
||||
return decBytes[x+1:] #Return everything after the separator
|
||||
|
||||
def _rawPrivateKeyOp(self, m):
|
||||
raise NotImplementedError()
|
||||
|
||||
def _rawPublicKeyOp(self, c):
|
||||
raise NotImplementedError()
|
||||
|
||||
def acceptsPassword(self):
|
||||
"""Return True if the write() method accepts a password for use
|
||||
in encrypting the private key.
|
||||
|
||||
@rtype: bool
|
||||
"""
|
||||
raise NotImplementedError()
|
||||
|
||||
def write(self, password=None):
|
||||
"""Return a string containing the key.
|
||||
|
||||
@rtype: str
|
||||
@return: A string describing the key, in whichever format (PEM
|
||||
or XML) is native to the implementation.
|
||||
"""
|
||||
raise NotImplementedError()
|
||||
|
||||
def writeXMLPublicKey(self, indent=''):
|
||||
"""Return a string containing the key.
|
||||
|
||||
@rtype: str
|
||||
@return: A string describing the public key, in XML format.
|
||||
"""
|
||||
return Python_RSAKey(self.n, self.e).write(indent)
|
||||
|
||||
def generate(bits):
|
||||
"""Generate a new key with the specified bit length.
|
||||
|
||||
@rtype: L{tlslite.utils.RSAKey.RSAKey}
|
||||
"""
|
||||
raise NotImplementedError()
|
||||
generate = staticmethod(generate)
|
||||
|
||||
|
||||
# **************************************************************************
|
||||
# Helper Functions for RSA Keys
|
||||
# **************************************************************************
|
||||
|
||||
def _addPKCS1SHA1Prefix(self, bytes):
|
||||
prefixBytes = createByteArraySequence(\
|
||||
[48,33,48,9,6,5,43,14,3,2,26,5,0,4,20])
|
||||
prefixedBytes = prefixBytes + bytes
|
||||
return prefixedBytes
|
||||
|
||||
def _addPKCS1Padding(self, bytes, blockType):
|
||||
padLength = (numBytes(self.n) - (len(bytes)+3))
|
||||
if blockType == 1: #Signature padding
|
||||
pad = [0xFF] * padLength
|
||||
elif blockType == 2: #Encryption padding
|
||||
pad = createByteArraySequence([])
|
||||
while len(pad) < padLength:
|
||||
padBytes = getRandomBytes(padLength * 2)
|
||||
pad = [b for b in padBytes if b != 0]
|
||||
pad = pad[:padLength]
|
||||
else:
|
||||
raise AssertionError()
|
||||
|
||||
#NOTE: To be proper, we should add [0,blockType]. However,
|
||||
#the zero is lost when the returned padding is converted
|
||||
#to a number, so we don't even bother with it. Also,
|
||||
#adding it would cause a misalignment in verify()
|
||||
padding = createByteArraySequence([blockType] + pad + [0])
|
||||
paddedBytes = padding + bytes
|
||||
return paddedBytes
|
||||
@@ -1,26 +0,0 @@
|
||||
"""Abstract class for 3DES."""
|
||||
|
||||
from .compat import * #For True
|
||||
|
||||
class TripleDES:
|
||||
def __init__(self, key, mode, IV, implementation):
|
||||
if len(key) != 24:
|
||||
raise ValueError()
|
||||
if mode != 2:
|
||||
raise ValueError()
|
||||
if len(IV) != 8:
|
||||
raise ValueError()
|
||||
self.isBlockCipher = True
|
||||
self.block_size = 8
|
||||
self.implementation = implementation
|
||||
self.name = "3des"
|
||||
|
||||
#CBC-Mode encryption, returns ciphertext
|
||||
#WARNING: *MAY* modify the input as well
|
||||
def encrypt(self, plaintext):
|
||||
assert(len(plaintext) % 8 == 0)
|
||||
|
||||
#CBC-Mode decryption, returns plaintext
|
||||
#WARNING: *MAY* modify the input as well
|
||||
def decrypt(self, ciphertext):
|
||||
assert(len(ciphertext) % 8 == 0)
|
||||
@@ -1,31 +0,0 @@
|
||||
"""Toolkit for crypto and other stuff."""
|
||||
|
||||
__all__ = ["AES",
|
||||
"ASN1Parser",
|
||||
"cipherfactory",
|
||||
"codec",
|
||||
"Cryptlib_AES",
|
||||
"Cryptlib_RC4",
|
||||
"Cryptlib_TripleDES",
|
||||
"cryptomath: cryptomath module",
|
||||
"dateFuncs",
|
||||
"hmac",
|
||||
"JCE_RSAKey",
|
||||
"compat",
|
||||
"keyfactory",
|
||||
"OpenSSL_AES",
|
||||
"OpenSSL_RC4",
|
||||
"OpenSSL_RSAKey",
|
||||
"OpenSSL_TripleDES",
|
||||
"PyCrypto_AES",
|
||||
"PyCrypto_RC4",
|
||||
"PyCrypto_RSAKey",
|
||||
"PyCrypto_TripleDES",
|
||||
"Python_AES",
|
||||
"Python_RC4",
|
||||
"Python_RSAKey",
|
||||
"RC4",
|
||||
"rijndael",
|
||||
"RSAKey",
|
||||
"TripleDES",
|
||||
"xmltools"]
|
||||
@@ -1,111 +0,0 @@
|
||||
"""Factory functions for symmetric cryptography."""
|
||||
|
||||
import os
|
||||
|
||||
from . import Python_AES
|
||||
from . import Python_RC4
|
||||
|
||||
from . import cryptomath
|
||||
|
||||
tripleDESPresent = False
|
||||
|
||||
if cryptomath.m2cryptoLoaded:
|
||||
from . import OpenSSL_AES
|
||||
from . import OpenSSL_RC4
|
||||
from . import OpenSSL_TripleDES
|
||||
tripleDESPresent = True
|
||||
|
||||
if cryptomath.cryptlibpyLoaded:
|
||||
from . import Cryptlib_AES
|
||||
from . import Cryptlib_RC4
|
||||
from . import Cryptlib_TripleDES
|
||||
tripleDESPresent = True
|
||||
|
||||
if cryptomath.pycryptoLoaded:
|
||||
from . import PyCrypto_AES
|
||||
from . import PyCrypto_RC4
|
||||
from . import PyCrypto_TripleDES
|
||||
tripleDESPresent = True
|
||||
|
||||
# **************************************************************************
|
||||
# Factory Functions for AES
|
||||
# **************************************************************************
|
||||
|
||||
def createAES(key, IV, implList=None):
|
||||
"""Create a new AES object.
|
||||
|
||||
@type key: str
|
||||
@param key: A 16, 24, or 32 byte string.
|
||||
|
||||
@type IV: str
|
||||
@param IV: A 16 byte string
|
||||
|
||||
@rtype: L{tlslite.utils.AES}
|
||||
@return: An AES object.
|
||||
"""
|
||||
if implList == None:
|
||||
implList = ["cryptlib", "openssl", "pycrypto", "python"]
|
||||
|
||||
for impl in implList:
|
||||
if impl == "cryptlib" and cryptomath.cryptlibpyLoaded:
|
||||
return Cryptlib_AES.new(key, 2, IV)
|
||||
elif impl == "openssl" and cryptomath.m2cryptoLoaded:
|
||||
return OpenSSL_AES.new(key, 2, IV)
|
||||
elif impl == "pycrypto" and cryptomath.pycryptoLoaded:
|
||||
return PyCrypto_AES.new(key, 2, IV)
|
||||
elif impl == "python":
|
||||
return Python_AES.new(key, 2, IV)
|
||||
raise NotImplementedError()
|
||||
|
||||
def createRC4(key, IV, implList=None):
|
||||
"""Create a new RC4 object.
|
||||
|
||||
@type key: str
|
||||
@param key: A 16 to 32 byte string.
|
||||
|
||||
@type IV: object
|
||||
@param IV: Ignored, whatever it is.
|
||||
|
||||
@rtype: L{tlslite.utils.RC4}
|
||||
@return: An RC4 object.
|
||||
"""
|
||||
if implList == None:
|
||||
implList = ["cryptlib", "openssl", "pycrypto", "python"]
|
||||
|
||||
if len(IV) != 0:
|
||||
raise AssertionError()
|
||||
for impl in implList:
|
||||
if impl == "cryptlib" and cryptomath.cryptlibpyLoaded:
|
||||
return Cryptlib_RC4.new(key)
|
||||
elif impl == "openssl" and cryptomath.m2cryptoLoaded:
|
||||
return OpenSSL_RC4.new(key)
|
||||
elif impl == "pycrypto" and cryptomath.pycryptoLoaded:
|
||||
return PyCrypto_RC4.new(key)
|
||||
elif impl == "python":
|
||||
return Python_RC4.new(key)
|
||||
raise NotImplementedError()
|
||||
|
||||
#Create a new TripleDES instance
|
||||
def createTripleDES(key, IV, implList=None):
|
||||
"""Create a new 3DES object.
|
||||
|
||||
@type key: str
|
||||
@param key: A 24 byte string.
|
||||
|
||||
@type IV: str
|
||||
@param IV: An 8 byte string
|
||||
|
||||
@rtype: L{tlslite.utils.TripleDES}
|
||||
@return: A 3DES object.
|
||||
"""
|
||||
if implList == None:
|
||||
implList = ["cryptlib", "openssl", "pycrypto"]
|
||||
|
||||
for impl in implList:
|
||||
if impl == "cryptlib" and cryptomath.cryptlibpyLoaded:
|
||||
return Cryptlib_TripleDES.new(key, 2, IV)
|
||||
elif impl == "openssl" and cryptomath.m2cryptoLoaded:
|
||||
return OpenSSL_TripleDES.new(key, 2, IV)
|
||||
elif impl == "pycrypto" and cryptomath.pycryptoLoaded:
|
||||
return PyCrypto_TripleDES.new(key, 2, IV)
|
||||
raise NotImplementedError()
|
||||
@@ -1,94 +0,0 @@
|
||||
"""Classes for reading/writing binary data (such as TLS records)."""
|
||||
|
||||
from .compat import *
|
||||
|
||||
class Writer:
|
||||
def __init__(self, length=0):
|
||||
#If length is zero, then this is just a "trial run" to determine length
|
||||
self.index = 0
|
||||
self.bytes = createByteArrayZeros(length)
|
||||
|
||||
def add(self, x, length):
|
||||
if self.bytes:
|
||||
newIndex = self.index+length-1
|
||||
while newIndex >= self.index:
|
||||
self.bytes[newIndex] = x & 0xFF
|
||||
x >>= 8
|
||||
newIndex -= 1
|
||||
self.index += length
|
||||
|
||||
def addFixSeq(self, seq, length):
|
||||
if self.bytes:
|
||||
for e in seq:
|
||||
self.add(e, length)
|
||||
else:
|
||||
self.index += len(seq)*length
|
||||
|
||||
def addVarSeq(self, seq, length, lengthLength):
|
||||
if self.bytes:
|
||||
self.add(len(seq)*length, lengthLength)
|
||||
for e in seq:
|
||||
self.add(e, length)
|
||||
else:
|
||||
self.index += lengthLength + (len(seq)*length)
|
||||
|
||||
|
||||
class Parser:
|
||||
def __init__(self, bytes):
|
||||
self.bytes = bytes
|
||||
self.index = 0
|
||||
|
||||
def get(self, length):
|
||||
if self.index + length > len(self.bytes):
|
||||
raise SyntaxError()
|
||||
x = 0
|
||||
for count in range(length):
|
||||
x <<= 8
|
||||
x |= self.bytes[self.index]
|
||||
self.index += 1
|
||||
return x
|
||||
|
||||
def getFixBytes(self, lengthBytes):
|
||||
bytes = self.bytes[self.index : self.index+lengthBytes]
|
||||
self.index += lengthBytes
|
||||
return bytes
|
||||
|
||||
def getVarBytes(self, lengthLength):
|
||||
lengthBytes = self.get(lengthLength)
|
||||
return self.getFixBytes(lengthBytes)
|
||||
|
||||
def getFixList(self, length, lengthList):
|
||||
l = [0] * lengthList
|
||||
for x in range(lengthList):
|
||||
l[x] = self.get(length)
|
||||
return l
|
||||
|
||||
def getVarList(self, length, lengthLength):
|
||||
lengthList = self.get(lengthLength)
|
||||
if lengthList % length != 0:
|
||||
raise SyntaxError()
|
||||
lengthList = int(lengthList/length)
|
||||
l = [0] * lengthList
|
||||
for x in range(lengthList):
|
||||
l[x] = self.get(length)
|
||||
return l
|
||||
|
||||
def startLengthCheck(self, lengthLength):
|
||||
self.lengthCheck = self.get(lengthLength)
|
||||
self.indexCheck = self.index
|
||||
|
||||
def setLengthCheck(self, length):
|
||||
self.lengthCheck = length
|
||||
self.indexCheck = self.index
|
||||
|
||||
def stopLengthCheck(self):
|
||||
if (self.index - self.indexCheck) != self.lengthCheck:
|
||||
raise SyntaxError()
|
||||
|
||||
def atLengthCheck(self):
|
||||
if (self.index - self.indexCheck) < self.lengthCheck:
|
||||
return False
|
||||
elif (self.index - self.indexCheck) == self.lengthCheck:
|
||||
return True
|
||||
else:
|
||||
raise SyntaxError()
|
||||
@@ -1,140 +0,0 @@
|
||||
"""Miscellaneous functions to mask Python version differences."""
|
||||
|
||||
import sys
|
||||
import os
|
||||
|
||||
if sys.version_info < (2,2):
|
||||
raise AssertionError("Python 2.2 or later required")
|
||||
|
||||
if sys.version_info < (2,3):
|
||||
|
||||
def enumerate(collection):
|
||||
return list(zip(list(range(len(collection))), collection))
|
||||
|
||||
class Set:
|
||||
def __init__(self, seq=None):
|
||||
self.values = {}
|
||||
if seq:
|
||||
for e in seq:
|
||||
self.values[e] = None
|
||||
|
||||
def add(self, e):
|
||||
self.values[e] = None
|
||||
|
||||
def discard(self, e):
|
||||
if e in list(self.values.keys()):
|
||||
del(self.values[e])
|
||||
|
||||
def union(self, s):
|
||||
ret = Set()
|
||||
for e in list(self.values.keys()):
|
||||
ret.values[e] = None
|
||||
for e in list(s.values.keys()):
|
||||
ret.values[e] = None
|
||||
return ret
|
||||
|
||||
def issubset(self, other):
|
||||
for e in list(self.values.keys()):
|
||||
if e not in list(other.values.keys()):
|
||||
return False
|
||||
return True
|
||||
|
||||
def __bool__( self):
|
||||
return len(list(self.values.keys()))
|
||||
|
||||
def __contains__(self, e):
|
||||
return e in list(self.values.keys())
|
||||
|
||||
def __iter__(self):
|
||||
return iter(list(set.values.keys()))
|
||||
|
||||
|
||||
if os.name != "java":
|
||||
|
||||
import array
|
||||
def createByteArraySequence(seq):
|
||||
return array.array('B', seq)
|
||||
def createByteArrayZeros(howMany):
|
||||
return array.array('B', [0] * howMany)
|
||||
def concatArrays(a1, a2):
|
||||
return a1+a2
|
||||
|
||||
def bytesToString(bytes):
|
||||
return bytes.tostring()
|
||||
def stringToBytes(s):
|
||||
bytes = createByteArrayZeros(0)
|
||||
bytes.fromstring(s)
|
||||
return bytes
|
||||
|
||||
import math
|
||||
def numBits(n):
|
||||
if n==0:
|
||||
return 0
|
||||
s = "%x" % n
|
||||
return ((len(s)-1)*4) + \
|
||||
{'0':0, '1':1, '2':2, '3':2,
|
||||
'4':3, '5':3, '6':3, '7':3,
|
||||
'8':4, '9':4, 'a':4, 'b':4,
|
||||
'c':4, 'd':4, 'e':4, 'f':4,
|
||||
}[s[0]]
|
||||
return int(math.floor(math.log(n, 2))+1)
|
||||
|
||||
BaseException = Exception
|
||||
import sys
|
||||
import traceback
|
||||
def formatExceptionTrace(e):
|
||||
newStr = "".join(traceback.format_exception(sys.exc_info()[0], sys.exc_info()[1], sys.exc_info()[2]))
|
||||
return newStr
|
||||
|
||||
else:
|
||||
#Jython 2.1 is missing lots of python 2.3 stuff,
|
||||
#which we have to emulate here:
|
||||
#NOTE: JYTHON SUPPORT NO LONGER WORKS, DUE TO USE OF GENERATORS.
|
||||
#THIS CODE IS LEFT IN SO THAT ONE JYTHON UPDATES TO 2.2, IT HAS A
|
||||
#CHANCE OF WORKING AGAIN.
|
||||
|
||||
import java
|
||||
import jarray
|
||||
|
||||
def createByteArraySequence(seq):
|
||||
if isinstance(seq, type("")): #If it's a string, convert
|
||||
seq = [ord(c) for c in seq]
|
||||
return jarray.array(seq, 'h') #use short instead of bytes, cause bytes are signed
|
||||
def createByteArrayZeros(howMany):
|
||||
return jarray.zeros(howMany, 'h') #use short instead of bytes, cause bytes are signed
|
||||
def concatArrays(a1, a2):
|
||||
l = list(a1)+list(a2)
|
||||
return createByteArraySequence(l)
|
||||
|
||||
#WAY TOO SLOW - MUST BE REPLACED------------
|
||||
def bytesToString(bytes):
|
||||
return "".join([chr(b) for b in bytes])
|
||||
|
||||
def stringToBytes(s):
|
||||
bytes = createByteArrayZeros(len(s))
|
||||
for count, c in enumerate(s):
|
||||
bytes[count] = ord(c)
|
||||
return bytes
|
||||
#WAY TOO SLOW - MUST BE REPLACED------------
|
||||
|
||||
def numBits(n):
|
||||
if n==0:
|
||||
return 0
|
||||
n= 1 * n; #convert to long, if it isn't already
|
||||
return n.__tojava__(java.math.BigInteger).bitLength()
|
||||
|
||||
#Adjust the string to an array of bytes
|
||||
def stringToJavaByteArray(s):
|
||||
bytes = jarray.zeros(len(s), 'b')
|
||||
for count, c in enumerate(s):
|
||||
x = ord(c)
|
||||
if x >= 128: x -= 256
|
||||
bytes[count] = x
|
||||
return bytes
|
||||
|
||||
BaseException = java.lang.Exception
|
||||
import sys
|
||||
import traceback
|
||||
def formatExceptionTrace(e):
|
||||
newStr = "".join(traceback.format_exception(sys.exc_info()[0], sys.exc_info()[1], sys.exc_info()[2]))
|
||||
return newStr
|
||||
@@ -1,404 +0,0 @@
|
||||
"""cryptomath module
|
||||
|
||||
This module has basic math/crypto code."""
|
||||
|
||||
import os
|
||||
import sys
|
||||
import math
|
||||
import base64
|
||||
import binascii
|
||||
if sys.version_info[:2] <= (2, 4):
|
||||
from sha import sha as sha1
|
||||
else:
|
||||
from hashlib import sha1
|
||||
|
||||
from .compat import *
|
||||
|
||||
|
||||
# **************************************************************************
|
||||
# Load Optional Modules
|
||||
# **************************************************************************
|
||||
|
||||
# Try to load M2Crypto/OpenSSL
|
||||
try:
|
||||
from M2Crypto import m2
|
||||
m2cryptoLoaded = True
|
||||
|
||||
except ImportError:
|
||||
m2cryptoLoaded = False
|
||||
|
||||
|
||||
# Try to load cryptlib
|
||||
try:
|
||||
import cryptlib_py
|
||||
try:
|
||||
cryptlib_py.cryptInit()
|
||||
except cryptlib_py.CryptException as e:
|
||||
#If tlslite and cryptoIDlib are both present,
|
||||
#they might each try to re-initialize this,
|
||||
#so we're tolerant of that.
|
||||
if e[0] != cryptlib_py.CRYPT_ERROR_INITED:
|
||||
raise
|
||||
cryptlibpyLoaded = True
|
||||
|
||||
except ImportError:
|
||||
cryptlibpyLoaded = False
|
||||
|
||||
#Try to load GMPY
|
||||
try:
|
||||
import gmpy
|
||||
gmpyLoaded = True
|
||||
except ImportError:
|
||||
gmpyLoaded = False
|
||||
|
||||
#Try to load pycrypto
|
||||
try:
|
||||
import Crypto.Cipher.AES
|
||||
pycryptoLoaded = True
|
||||
except ImportError:
|
||||
pycryptoLoaded = False
|
||||
|
||||
|
||||
# **************************************************************************
|
||||
# PRNG Functions
|
||||
# **************************************************************************
|
||||
|
||||
# Get os.urandom PRNG
|
||||
try:
|
||||
os.urandom(1)
|
||||
def getRandomBytes(howMany):
|
||||
return stringToBytes(os.urandom(howMany))
|
||||
prngName = "os.urandom"
|
||||
|
||||
except:
|
||||
# Else get cryptlib PRNG
|
||||
if cryptlibpyLoaded:
|
||||
def getRandomBytes(howMany):
|
||||
randomKey = cryptlib_py.cryptCreateContext(cryptlib_py.CRYPT_UNUSED,
|
||||
cryptlib_py.CRYPT_ALGO_AES)
|
||||
cryptlib_py.cryptSetAttribute(randomKey,
|
||||
cryptlib_py.CRYPT_CTXINFO_MODE,
|
||||
cryptlib_py.CRYPT_MODE_OFB)
|
||||
cryptlib_py.cryptGenerateKey(randomKey)
|
||||
bytes = createByteArrayZeros(howMany)
|
||||
cryptlib_py.cryptEncrypt(randomKey, bytes)
|
||||
return bytes
|
||||
prngName = "cryptlib"
|
||||
|
||||
else:
|
||||
#Else get UNIX /dev/urandom PRNG
|
||||
try:
|
||||
devRandomFile = open("/dev/urandom", "rb")
|
||||
def getRandomBytes(howMany):
|
||||
return stringToBytes(devRandomFile.read(howMany))
|
||||
prngName = "/dev/urandom"
|
||||
except IOError:
|
||||
#Else get Win32 CryptoAPI PRNG
|
||||
try:
|
||||
import win32prng
|
||||
def getRandomBytes(howMany):
|
||||
s = win32prng.getRandomBytes(howMany)
|
||||
if len(s) != howMany:
|
||||
raise AssertionError()
|
||||
return stringToBytes(s)
|
||||
prngName ="CryptoAPI"
|
||||
except ImportError:
|
||||
#Else no PRNG :-(
|
||||
def getRandomBytes(howMany):
|
||||
raise NotImplementedError("No Random Number Generator "\
|
||||
"available.")
|
||||
prngName = "None"
|
||||
|
||||
# **************************************************************************
|
||||
# Converter Functions
|
||||
# **************************************************************************
|
||||
|
||||
def bytesToNumber(bytes):
|
||||
total = 0
|
||||
multiplier = 1
|
||||
for count in range(len(bytes)-1, -1, -1):
|
||||
byte = bytes[count]
|
||||
total += multiplier * byte
|
||||
multiplier *= 256
|
||||
return total
|
||||
|
||||
def numberToBytes(n):
|
||||
howManyBytes = numBytes(n)
|
||||
bytes = createByteArrayZeros(howManyBytes)
|
||||
for count in range(howManyBytes-1, -1, -1):
|
||||
bytes[count] = int(n % 256)
|
||||
n >>= 8
|
||||
return bytes
|
||||
|
||||
def bytesToBase64(bytes):
|
||||
s = bytesToString(bytes)
|
||||
return stringToBase64(s)
|
||||
|
||||
def base64ToBytes(s):
|
||||
s = base64ToString(s)
|
||||
return stringToBytes(s)
|
||||
|
||||
def numberToBase64(n):
|
||||
bytes = numberToBytes(n)
|
||||
return bytesToBase64(bytes)
|
||||
|
||||
def base64ToNumber(s):
|
||||
bytes = base64ToBytes(s)
|
||||
return bytesToNumber(bytes)
|
||||
|
||||
def stringToNumber(s):
|
||||
bytes = stringToBytes(s)
|
||||
return bytesToNumber(bytes)
|
||||
|
||||
def numberToString(s):
|
||||
bytes = numberToBytes(s)
|
||||
return bytesToString(bytes)
|
||||
|
||||
def base64ToString(s):
|
||||
try:
|
||||
return base64.decodestring(s)
|
||||
except binascii.Error as e:
|
||||
raise SyntaxError(e)
|
||||
except binascii.Incomplete as e:
|
||||
raise SyntaxError(e)
|
||||
|
||||
def stringToBase64(s):
|
||||
return base64.encodebytes(s).replace("\n", "")
|
||||
|
||||
def mpiToNumber(mpi): #mpi is an openssl-format bignum string
|
||||
if (ord(mpi[4]) & 0x80) !=0: #Make sure this is a positive number
|
||||
raise AssertionError()
|
||||
bytes = stringToBytes(mpi[4:])
|
||||
return bytesToNumber(bytes)
|
||||
|
||||
def numberToMPI(n):
|
||||
bytes = numberToBytes(n)
|
||||
ext = 0
|
||||
#If the high-order bit is going to be set,
|
||||
#add an extra byte of zeros
|
||||
if (numBits(n) & 0x7)==0:
|
||||
ext = 1
|
||||
length = numBytes(n) + ext
|
||||
bytes = concatArrays(createByteArrayZeros(4+ext), bytes)
|
||||
bytes[0] = (length >> 24) & 0xFF
|
||||
bytes[1] = (length >> 16) & 0xFF
|
||||
bytes[2] = (length >> 8) & 0xFF
|
||||
bytes[3] = length & 0xFF
|
||||
return bytesToString(bytes)
|
||||
|
||||
|
||||
|
||||
# **************************************************************************
|
||||
# Misc. Utility Functions
|
||||
# **************************************************************************
|
||||
|
||||
def numBytes(n):
|
||||
if n==0:
|
||||
return 0
|
||||
bits = numBits(n)
|
||||
return int(math.ceil(bits / 8.0))
|
||||
|
||||
def hashAndBase64(s):
|
||||
return stringToBase64(sha1(s).digest())
|
||||
|
||||
def getBase64Nonce(numChars=22): #defaults to an 132 bit nonce
|
||||
bytes = getRandomBytes(numChars)
|
||||
bytesStr = "".join([chr(b) for b in bytes])
|
||||
return stringToBase64(bytesStr)[:numChars]
|
||||
|
||||
|
||||
# **************************************************************************
|
||||
# Big Number Math
|
||||
# **************************************************************************
|
||||
|
||||
def getRandomNumber(low, high):
|
||||
if low >= high:
|
||||
raise AssertionError()
|
||||
howManyBits = numBits(high)
|
||||
howManyBytes = numBytes(high)
|
||||
lastBits = howManyBits % 8
|
||||
while 1:
|
||||
bytes = getRandomBytes(howManyBytes)
|
||||
if lastBits:
|
||||
bytes[0] = bytes[0] % (1 << lastBits)
|
||||
n = bytesToNumber(bytes)
|
||||
if n >= low and n < high:
|
||||
return n
|
||||
|
||||
def gcd(a,b):
|
||||
a, b = max(a,b), min(a,b)
|
||||
while b:
|
||||
a, b = b, a % b
|
||||
return a
|
||||
|
||||
def lcm(a, b):
|
||||
#This will break when python division changes, but we can't use // cause
|
||||
#of Jython
|
||||
return (a * b) / gcd(a, b)
|
||||
|
||||
#Returns inverse of a mod b, zero if none
|
||||
#Uses Extended Euclidean Algorithm
|
||||
def invMod(a, b):
|
||||
c, d = a, b
|
||||
uc, ud = 1, 0
|
||||
while c != 0:
|
||||
#This will break when python division changes, but we can't use //
|
||||
#cause of Jython
|
||||
q = d / c
|
||||
c, d = d-(q*c), c
|
||||
uc, ud = ud - (q * uc), uc
|
||||
if d == 1:
|
||||
return ud % b
|
||||
return 0
|
||||
|
||||
|
||||
if gmpyLoaded:
|
||||
def powMod(base, power, modulus):
|
||||
base = gmpy.mpz(base)
|
||||
power = gmpy.mpz(power)
|
||||
modulus = gmpy.mpz(modulus)
|
||||
result = pow(base, power, modulus)
|
||||
return int(result)
|
||||
|
||||
else:
|
||||
#Copied from Bryan G. Olson's post to comp.lang.python
|
||||
#Does left-to-right instead of pow()'s right-to-left,
|
||||
#thus about 30% faster than the python built-in with small bases
|
||||
def powMod(base, power, modulus):
|
||||
nBitScan = 5
|
||||
|
||||
""" Return base**power mod modulus, using multi bit scanning
|
||||
with nBitScan bits at a time."""
|
||||
|
||||
#TREV - Added support for negative exponents
|
||||
negativeResult = False
|
||||
if (power < 0):
|
||||
power *= -1
|
||||
negativeResult = True
|
||||
|
||||
exp2 = 2**nBitScan
|
||||
mask = exp2 - 1
|
||||
|
||||
# Break power into a list of digits of nBitScan bits.
|
||||
# The list is recursive so easy to read in reverse direction.
|
||||
nibbles = None
|
||||
while power:
|
||||
nibbles = int(power & mask), nibbles
|
||||
power = power >> nBitScan
|
||||
|
||||
# Make a table of powers of base up to 2**nBitScan - 1
|
||||
lowPowers = [1]
|
||||
for i in range(1, exp2):
|
||||
lowPowers.append((lowPowers[i-1] * base) % modulus)
|
||||
|
||||
# To exponentiate by the first nibble, look it up in the table
|
||||
nib, nibbles = nibbles
|
||||
prod = lowPowers[nib]
|
||||
|
||||
# For the rest, square nBitScan times, then multiply by
|
||||
# base^nibble
|
||||
while nibbles:
|
||||
nib, nibbles = nibbles
|
||||
for i in range(nBitScan):
|
||||
prod = (prod * prod) % modulus
|
||||
if nib: prod = (prod * lowPowers[nib]) % modulus
|
||||
|
||||
#TREV - Added support for negative exponents
|
||||
if negativeResult:
|
||||
prodInv = invMod(prod, modulus)
|
||||
#Check to make sure the inverse is correct
|
||||
if (prod * prodInv) % modulus != 1:
|
||||
raise AssertionError()
|
||||
return prodInv
|
||||
return prod
|
||||
|
||||
|
||||
#Pre-calculate a sieve of the ~100 primes < 1000:
|
||||
def makeSieve(n):
|
||||
sieve = list(range(n))
|
||||
for count in range(2, int(math.sqrt(n))):
|
||||
if sieve[count] == 0:
|
||||
continue
|
||||
x = sieve[count] * 2
|
||||
while x < len(sieve):
|
||||
sieve[x] = 0
|
||||
x += sieve[count]
|
||||
sieve = [x for x in sieve[2:] if x]
|
||||
return sieve
|
||||
|
||||
sieve = makeSieve(1000)
|
||||
|
||||
def isPrime(n, iterations=5, display=False):
|
||||
#Trial division with sieve
|
||||
for x in sieve:
|
||||
if x >= n: return True
|
||||
if n % x == 0: return False
|
||||
#Passed trial division, proceed to Rabin-Miller
|
||||
#Rabin-Miller implemented per Ferguson & Schneier
|
||||
#Compute s, t for Rabin-Miller
|
||||
if display: print("*", end=' ')
|
||||
s, t = n-1, 0
|
||||
while s % 2 == 0:
|
||||
s, t = s/2, t+1
|
||||
#Repeat Rabin-Miller x times
|
||||
a = 2 #Use 2 as a base for first iteration speedup, per HAC
|
||||
for count in range(iterations):
|
||||
v = powMod(a, s, n)
|
||||
if v==1:
|
||||
continue
|
||||
i = 0
|
||||
while v != n-1:
|
||||
if i == t-1:
|
||||
return False
|
||||
else:
|
||||
v, i = powMod(v, 2, n), i+1
|
||||
a = getRandomNumber(2, n)
|
||||
return True
|
||||
|
||||
def getRandomPrime(bits, display=False):
|
||||
if bits < 10:
|
||||
raise AssertionError()
|
||||
#The 1.5 ensures the 2 MSBs are set
|
||||
#Thus, when used for p,q in RSA, n will have its MSB set
|
||||
#
|
||||
#Since 30 is lcm(2,3,5), we'll set our test numbers to
|
||||
#29 % 30 and keep them there
|
||||
low = (2 ** (bits-1)) * 3/2
|
||||
high = 2 ** bits - 30
|
||||
p = getRandomNumber(low, high)
|
||||
p += 29 - (p % 30)
|
||||
while 1:
|
||||
if display: print(".", end=' ')
|
||||
p += 30
|
||||
if p >= high:
|
||||
p = getRandomNumber(low, high)
|
||||
p += 29 - (p % 30)
|
||||
if isPrime(p, display=display):
|
||||
return p
|
||||
|
||||
#Unused at the moment...
|
||||
def getRandomSafePrime(bits, display=False):
|
||||
if bits < 10:
|
||||
raise AssertionError()
|
||||
#The 1.5 ensures the 2 MSBs are set
|
||||
#Thus, when used for p,q in RSA, n will have its MSB set
|
||||
#
|
||||
#Since 30 is lcm(2,3,5), we'll set our test numbers to
|
||||
#29 % 30 and keep them there
|
||||
low = (2 ** (bits-2)) * 3/2
|
||||
high = (2 ** (bits-1)) - 30
|
||||
q = getRandomNumber(low, high)
|
||||
q += 29 - (q % 30)
|
||||
while 1:
|
||||
if display: print(".", end=' ')
|
||||
q += 30
|
||||
if (q >= high):
|
||||
q = getRandomNumber(low, high)
|
||||
q += 29 - (q % 30)
|
||||
#Ideas from Tom Wu's SRP code
|
||||
#Do trial division on p and q before Rabin-Miller
|
||||
if isPrime(q, 0, display=display):
|
||||
p = (2 * q) + 1
|
||||
if isPrime(p, display=display):
|
||||
if isPrime(q, display=display):
|
||||
return p
|
||||
@@ -1,75 +0,0 @@
|
||||
|
||||
import os
|
||||
|
||||
#Functions for manipulating datetime objects
|
||||
#CCYY-MM-DDThh:mm:ssZ
|
||||
def parseDateClass(s):
|
||||
year, month, day = s.split("-")
|
||||
day, tail = day[:2], day[2:]
|
||||
hour, minute, second = tail[1:].split(":")
|
||||
second = second[:2]
|
||||
year, month, day = int(year), int(month), int(day)
|
||||
hour, minute, second = int(hour), int(minute), int(second)
|
||||
return createDateClass(year, month, day, hour, minute, second)
|
||||
|
||||
|
||||
if os.name != "java":
|
||||
from datetime import datetime, timedelta
|
||||
|
||||
#Helper functions for working with a date/time class
|
||||
def createDateClass(year, month, day, hour, minute, second):
|
||||
return datetime(year, month, day, hour, minute, second)
|
||||
|
||||
def printDateClass(d):
|
||||
#Split off fractional seconds, append 'Z'
|
||||
return d.isoformat().split(".")[0]+"Z"
|
||||
|
||||
def getNow():
|
||||
return datetime.utcnow()
|
||||
|
||||
def getHoursFromNow(hours):
|
||||
return datetime.utcnow() + timedelta(hours=hours)
|
||||
|
||||
def getMinutesFromNow(minutes):
|
||||
return datetime.utcnow() + timedelta(minutes=minutes)
|
||||
|
||||
def isDateClassExpired(d):
|
||||
return d < datetime.utcnow()
|
||||
|
||||
def isDateClassBefore(d1, d2):
|
||||
return d1 < d2
|
||||
|
||||
else:
|
||||
#Jython 2.1 is missing lots of python 2.3 stuff,
|
||||
#which we have to emulate here:
|
||||
import java
|
||||
import jarray
|
||||
|
||||
def createDateClass(year, month, day, hour, minute, second):
|
||||
c = java.util.Calendar.getInstance()
|
||||
c.setTimeZone(java.util.TimeZone.getTimeZone("UTC"))
|
||||
c.set(year, month-1, day, hour, minute, second)
|
||||
return c
|
||||
|
||||
def printDateClass(d):
|
||||
return "%04d-%02d-%02dT%02d:%02d:%02dZ" % \
|
||||
(d.get(d.YEAR), d.get(d.MONTH)+1, d.get(d.DATE), \
|
||||
d.get(d.HOUR_OF_DAY), d.get(d.MINUTE), d.get(d.SECOND))
|
||||
|
||||
def getNow():
|
||||
c = java.util.Calendar.getInstance()
|
||||
c.setTimeZone(java.util.TimeZone.getTimeZone("UTC"))
|
||||
c.get(c.HOUR) #force refresh?
|
||||
return c
|
||||
|
||||
def getHoursFromNow(hours):
|
||||
d = getNow()
|
||||
d.add(d.HOUR, hours)
|
||||
return d
|
||||
|
||||
def isDateClassExpired(d):
|
||||
n = getNow()
|
||||
return d.before(n)
|
||||
|
||||
def isDateClassBefore(d1, d2):
|
||||
return d1.before(d2)
|
||||
@@ -1,173 +0,0 @@
|
||||
|
||||
#include "Python.h"
|
||||
|
||||
|
||||
#ifdef MS_WINDOWS
|
||||
|
||||
/* The following #define is not needed on VC6 with the Platform SDK, and it
|
||||
may not be needed on VC7, I'm not sure. I don't think it hurts anything.*/
|
||||
#define _WIN32_WINNT 0x0400
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
|
||||
typedef BOOL (WINAPI *CRYPTACQUIRECONTEXTA)(HCRYPTPROV *phProv,\
|
||||
LPCSTR pszContainer, LPCSTR pszProvider, DWORD dwProvType,\
|
||||
DWORD dwFlags );
|
||||
typedef BOOL (WINAPI *CRYPTGENRANDOM)(HCRYPTPROV hProv, DWORD dwLen,\
|
||||
BYTE *pbBuffer );
|
||||
typedef BOOL (WINAPI *CRYPTRELEASECONTEXT)(HCRYPTPROV hProv,\
|
||||
DWORD dwFlags);
|
||||
|
||||
|
||||
static PyObject* entropy(PyObject *self, PyObject *args)
|
||||
{
|
||||
int howMany = 0;
|
||||
HINSTANCE hAdvAPI32 = NULL;
|
||||
CRYPTACQUIRECONTEXTA pCryptAcquireContextA = NULL;
|
||||
CRYPTGENRANDOM pCryptGenRandom = NULL;
|
||||
CRYPTRELEASECONTEXT pCryptReleaseContext = NULL;
|
||||
HCRYPTPROV hCryptProv = 0;
|
||||
unsigned char* bytes = NULL;
|
||||
PyObject* returnVal = NULL;
|
||||
|
||||
|
||||
/* Read arguments */
|
||||
if (!PyArg_ParseTuple(args, "i", &howMany))
|
||||
return(NULL);
|
||||
|
||||
/* Obtain handle to the DLL containing CryptoAPI
|
||||
This should not fail */
|
||||
if( (hAdvAPI32 = GetModuleHandle("advapi32.dll")) == NULL) {
|
||||
PyErr_Format(PyExc_SystemError,
|
||||
"Advapi32.dll not found");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Obtain pointers to the CryptoAPI functions
|
||||
This will fail on some early version of Win95 */
|
||||
pCryptAcquireContextA = (CRYPTACQUIRECONTEXTA)GetProcAddress(hAdvAPI32,\
|
||||
"CryptAcquireContextA");
|
||||
pCryptGenRandom = (CRYPTGENRANDOM)GetProcAddress(hAdvAPI32,\
|
||||
"CryptGenRandom");
|
||||
pCryptReleaseContext = (CRYPTRELEASECONTEXT) GetProcAddress(hAdvAPI32,\
|
||||
"CryptReleaseContext");
|
||||
if (pCryptAcquireContextA == NULL || pCryptGenRandom == NULL ||
|
||||
pCryptReleaseContext == NULL) {
|
||||
PyErr_Format(PyExc_NotImplementedError,
|
||||
"CryptoAPI not available on this version of Windows");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Allocate bytes */
|
||||
if ((bytes = (unsigned char*)PyMem_Malloc(howMany)) == NULL)
|
||||
return PyErr_NoMemory();
|
||||
|
||||
|
||||
/* Acquire context */
|
||||
if(!pCryptAcquireContextA(&hCryptProv, NULL, NULL, PROV_RSA_FULL,
|
||||
CRYPT_VERIFYCONTEXT)) {
|
||||
PyErr_Format(PyExc_SystemError,
|
||||
"CryptAcquireContext failed, error %d", GetLastError());
|
||||
PyMem_Free(bytes);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Get random data */
|
||||
if(!pCryptGenRandom(hCryptProv, howMany, bytes)) {
|
||||
PyErr_Format(PyExc_SystemError,
|
||||
"CryptGenRandom failed, error %d", GetLastError());
|
||||
PyMem_Free(bytes);
|
||||
CryptReleaseContext(hCryptProv, 0);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Build return value */
|
||||
returnVal = Py_BuildValue("s#", bytes, howMany);
|
||||
PyMem_Free(bytes);
|
||||
|
||||
/* Release context */
|
||||
if (!pCryptReleaseContext(hCryptProv, 0)) {
|
||||
PyErr_Format(PyExc_SystemError,
|
||||
"CryptReleaseContext failed, error %d", GetLastError());
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return returnVal;
|
||||
}
|
||||
|
||||
#elif defined(HAVE_UNISTD_H) && defined(HAVE_FCNTL_H)
|
||||
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
|
||||
static PyObject* entropy(PyObject *self, PyObject *args)
|
||||
{
|
||||
int howMany;
|
||||
int fd;
|
||||
unsigned char* bytes = NULL;
|
||||
PyObject* returnVal = NULL;
|
||||
|
||||
|
||||
/* Read arguments */
|
||||
if (!PyArg_ParseTuple(args, "i", &howMany))
|
||||
return(NULL);
|
||||
|
||||
/* Allocate bytes */
|
||||
if ((bytes = (unsigned char*)PyMem_Malloc(howMany)) == NULL)
|
||||
return PyErr_NoMemory();
|
||||
|
||||
/* Open device */
|
||||
if ((fd = open("/dev/urandom", O_RDONLY, 0)) == -1) {
|
||||
PyErr_Format(PyExc_NotImplementedError,
|
||||
"No entropy source found");
|
||||
PyMem_Free(bytes);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Get random data */
|
||||
if (read(fd, bytes, howMany) < howMany) {
|
||||
PyErr_Format(PyExc_SystemError,
|
||||
"Reading from /dev/urandom failed");
|
||||
PyMem_Free(bytes);
|
||||
close(fd);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Build return value */
|
||||
returnVal = Py_BuildValue("s#", bytes, howMany);
|
||||
PyMem_Free(bytes);
|
||||
|
||||
/* Close device */
|
||||
close(fd);
|
||||
|
||||
return returnVal;
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
static PyObject* entropy(PyObject *self, PyObject *args)
|
||||
{
|
||||
PyErr_Format(PyExc_NotImplementedError,
|
||||
"Function not supported");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
/* List of functions exported by this module */
|
||||
|
||||
static struct PyMethodDef entropy_functions[] = {
|
||||
{"entropy", (PyCFunction)entropy, METH_VARARGS, "Return a string of random bytes produced by a platform-specific\nentropy source."},
|
||||
{NULL, NULL} /* Sentinel */
|
||||
};
|
||||
|
||||
|
||||
/* Initialize this module. */
|
||||
|
||||
PyMODINIT_FUNC initentropy(void)
|
||||
{
|
||||
Py_InitModule("entropy", entropy_functions);
|
||||
}
|
||||
@@ -1,104 +0,0 @@
|
||||
"""HMAC (Keyed-Hashing for Message Authentication) Python module.
|
||||
|
||||
Implements the HMAC algorithm as described by RFC 2104.
|
||||
|
||||
(This file is modified from the standard library version to do faster
|
||||
copying)
|
||||
"""
|
||||
|
||||
def _strxor(s1, s2):
|
||||
"""Utility method. XOR the two strings s1 and s2 (must have same length).
|
||||
"""
|
||||
return "".join(map(lambda x, y: chr(ord(x) ^ ord(y)), s1, s2))
|
||||
|
||||
# The size of the digests returned by HMAC depends on the underlying
|
||||
# hashing module used.
|
||||
digest_size = None
|
||||
|
||||
class HMAC:
|
||||
"""RFC2104 HMAC class.
|
||||
|
||||
This supports the API for Cryptographic Hash Functions (PEP 247).
|
||||
"""
|
||||
|
||||
def __init__(self, key, msg = None, digestmod = None):
|
||||
"""Create a new HMAC object.
|
||||
|
||||
key: key for the keyed hash object.
|
||||
msg: Initial input for the hash, if provided.
|
||||
digestmod: A module supporting PEP 247. Defaults to the md5 module.
|
||||
"""
|
||||
if digestmod is None:
|
||||
import md5
|
||||
digestmod = md5
|
||||
|
||||
if key == None: #TREVNEW - for faster copying
|
||||
return #TREVNEW
|
||||
|
||||
self.digestmod = digestmod
|
||||
self.outer = digestmod.new()
|
||||
self.inner = digestmod.new()
|
||||
self.digest_size = digestmod.digest_size
|
||||
|
||||
blocksize = 64
|
||||
ipad = "\x36" * blocksize
|
||||
opad = "\x5C" * blocksize
|
||||
|
||||
if len(key) > blocksize:
|
||||
key = digestmod.new(key).digest()
|
||||
|
||||
key = key + chr(0) * (blocksize - len(key))
|
||||
self.outer.update(_strxor(key, opad))
|
||||
self.inner.update(_strxor(key, ipad))
|
||||
if msg is not None:
|
||||
self.update(msg)
|
||||
|
||||
## def clear(self):
|
||||
## raise NotImplementedError, "clear() method not available in HMAC."
|
||||
|
||||
def update(self, msg):
|
||||
"""Update this hashing object with the string msg.
|
||||
"""
|
||||
self.inner.update(msg)
|
||||
|
||||
def copy(self):
|
||||
"""Return a separate copy of this hashing object.
|
||||
|
||||
An update to this copy won't affect the original object.
|
||||
"""
|
||||
other = HMAC(None) #TREVNEW - for faster copying
|
||||
other.digest_size = self.digest_size #TREVNEW
|
||||
other.digestmod = self.digestmod
|
||||
other.inner = self.inner.copy()
|
||||
other.outer = self.outer.copy()
|
||||
return other
|
||||
|
||||
def digest(self):
|
||||
"""Return the hash value of this hashing object.
|
||||
|
||||
This returns a string containing 8-bit data. The object is
|
||||
not altered in any way by this function; you can continue
|
||||
updating the object after calling this function.
|
||||
"""
|
||||
h = self.outer.copy()
|
||||
h.update(self.inner.digest())
|
||||
return h.digest()
|
||||
|
||||
def hexdigest(self):
|
||||
"""Like digest(), but returns a string of hexadecimal digits instead.
|
||||
"""
|
||||
return "".join([hex(ord(x))[2:].zfill(2)
|
||||
for x in tuple(self.digest())])
|
||||
|
||||
def new(key, msg = None, digestmod = None):
|
||||
"""Create a new hashing object and return it.
|
||||
|
||||
key: The starting key for the hash.
|
||||
msg: if available, will immediately be hashed into the object's starting
|
||||
state.
|
||||
|
||||
You can now feed arbitrary strings into the object using its update()
|
||||
method, and can ask for the hash value at any time by calling its digest()
|
||||
method.
|
||||
"""
|
||||
return HMAC(key, msg, digestmod)
|
||||
@@ -1,195 +0,0 @@
|
||||
"""Miscellaneous functions to mask Python/Jython differences."""
|
||||
|
||||
import os
|
||||
import sha
|
||||
|
||||
if os.name != "java":
|
||||
BaseException = Exception
|
||||
|
||||
from sets import Set
|
||||
import array
|
||||
import math
|
||||
|
||||
def createByteArraySequence(seq):
|
||||
return array.array('B', seq)
|
||||
def createByteArrayZeros(howMany):
|
||||
return array.array('B', [0] * howMany)
|
||||
def concatArrays(a1, a2):
|
||||
return a1+a2
|
||||
|
||||
def bytesToString(bytes):
|
||||
return bytes.tostring()
|
||||
|
||||
def stringToBytes(s):
|
||||
bytes = createByteArrayZeros(0)
|
||||
bytes.fromstring(s)
|
||||
return bytes
|
||||
|
||||
def numBits(n):
|
||||
if n==0:
|
||||
return 0
|
||||
return int(math.floor(math.log(n, 2))+1)
|
||||
|
||||
class CertChainBase: pass
|
||||
class SelfTestBase: pass
|
||||
class ReportFuncBase: pass
|
||||
|
||||
#Helper functions for working with sets (from Python 2.3)
|
||||
def iterSet(set):
|
||||
return iter(set)
|
||||
|
||||
def getListFromSet(set):
|
||||
return list(set)
|
||||
|
||||
#Factory function for getting a SHA1 object
|
||||
def getSHA1(s):
|
||||
return sha.sha(s)
|
||||
|
||||
import sys
|
||||
import traceback
|
||||
|
||||
def formatExceptionTrace(e):
|
||||
newStr = "".join(traceback.format_exception(sys.exc_info()[0], sys.exc_info()[1], sys.exc_info()[2]))
|
||||
return newStr
|
||||
|
||||
else:
|
||||
#Jython 2.1 is missing lots of python 2.3 stuff,
|
||||
#which we have to emulate here:
|
||||
import java
|
||||
import jarray
|
||||
|
||||
BaseException = java.lang.Exception
|
||||
|
||||
def createByteArraySequence(seq):
|
||||
if isinstance(seq, type("")): #If it's a string, convert
|
||||
seq = [ord(c) for c in seq]
|
||||
return jarray.array(seq, 'h') #use short instead of bytes, cause bytes are signed
|
||||
def createByteArrayZeros(howMany):
|
||||
return jarray.zeros(howMany, 'h') #use short instead of bytes, cause bytes are signed
|
||||
def concatArrays(a1, a2):
|
||||
l = list(a1)+list(a2)
|
||||
return createByteArraySequence(l)
|
||||
|
||||
#WAY TOO SLOW - MUST BE REPLACED------------
|
||||
def bytesToString(bytes):
|
||||
return "".join([chr(b) for b in bytes])
|
||||
|
||||
def stringToBytes(s):
|
||||
bytes = createByteArrayZeros(len(s))
|
||||
for count, c in enumerate(s):
|
||||
bytes[count] = ord(c)
|
||||
return bytes
|
||||
#WAY TOO SLOW - MUST BE REPLACED------------
|
||||
|
||||
def numBits(n):
|
||||
if n==0:
|
||||
return 0
|
||||
n= 1 * n; #convert to long, if it isn't already
|
||||
return n.__tojava__(java.math.BigInteger).bitLength()
|
||||
|
||||
#This properly creates static methods for Jython
|
||||
class staticmethod:
|
||||
def __init__(self, anycallable): self.__call__ = anycallable
|
||||
|
||||
#Properties are not supported for Jython
|
||||
class property:
|
||||
def __init__(self, anycallable): pass
|
||||
|
||||
#True and False have to be specially defined
|
||||
False = 0
|
||||
True = 1
|
||||
|
||||
class StopIteration(Exception): pass
|
||||
|
||||
def enumerate(collection):
|
||||
return list(zip(list(range(len(collection))), collection))
|
||||
|
||||
class Set:
|
||||
def __init__(self, seq=None):
|
||||
self.values = {}
|
||||
if seq:
|
||||
for e in seq:
|
||||
self.values[e] = None
|
||||
|
||||
def add(self, e):
|
||||
self.values[e] = None
|
||||
|
||||
def discard(self, e):
|
||||
if e in list(self.values.keys()):
|
||||
del(self.values[e])
|
||||
|
||||
def union(self, s):
|
||||
ret = Set()
|
||||
for e in list(self.values.keys()):
|
||||
ret.values[e] = None
|
||||
for e in list(s.values.keys()):
|
||||
ret.values[e] = None
|
||||
return ret
|
||||
|
||||
def issubset(self, other):
|
||||
for e in list(self.values.keys()):
|
||||
if e not in list(other.values.keys()):
|
||||
return False
|
||||
return True
|
||||
|
||||
def __bool__( self):
|
||||
return len(list(self.values.keys()))
|
||||
|
||||
def __contains__(self, e):
|
||||
return e in list(self.values.keys())
|
||||
|
||||
def iterSet(set):
|
||||
return list(set.values.keys())
|
||||
|
||||
def getListFromSet(set):
|
||||
return list(set.values.keys())
|
||||
|
||||
"""
|
||||
class JCE_SHA1:
|
||||
def __init__(self, s=None):
|
||||
self.md = java.security.MessageDigest.getInstance("SHA1")
|
||||
if s:
|
||||
self.update(s)
|
||||
|
||||
def update(self, s):
|
||||
self.md.update(s)
|
||||
|
||||
def copy(self):
|
||||
sha1 = JCE_SHA1()
|
||||
sha1.md = self.md.clone()
|
||||
return sha1
|
||||
|
||||
def digest(self):
|
||||
digest = self.md.digest()
|
||||
bytes = jarray.zeros(20, 'h')
|
||||
for count in xrange(20):
|
||||
x = digest[count]
|
||||
if x < 0: x += 256
|
||||
bytes[count] = x
|
||||
return bytes
|
||||
"""
|
||||
|
||||
#Factory function for getting a SHA1 object
|
||||
#The JCE_SHA1 class is way too slow...
|
||||
#the sha.sha object we use instead is broken in the jython 2.1
|
||||
#release, and needs to be patched
|
||||
def getSHA1(s):
|
||||
#return JCE_SHA1(s)
|
||||
return sha.sha(s)
|
||||
|
||||
|
||||
#Adjust the string to an array of bytes
|
||||
def stringToJavaByteArray(s):
|
||||
bytes = jarray.zeros(len(s), 'b')
|
||||
for count, c in enumerate(s):
|
||||
x = ord(c)
|
||||
if x >= 128: x -= 256
|
||||
bytes[count] = x
|
||||
return bytes
|
||||
|
||||
import sys
|
||||
import traceback
|
||||
|
||||
def formatExceptionTrace(e):
|
||||
newStr = "".join(traceback.format_exception(sys.exc_info()[0], sys.exc_info()[1], sys.exc_info()[2]))
|
||||
return newStr
|
||||
@@ -1,243 +0,0 @@
|
||||
"""Factory functions for asymmetric cryptography.
|
||||
@sort: generateRSAKey, parseXMLKey, parsePEMKey, parseAsPublicKey,
|
||||
parseAsPrivateKey
|
||||
"""
|
||||
|
||||
from .compat import *
|
||||
|
||||
from .RSAKey import RSAKey
|
||||
from .Python_RSAKey import Python_RSAKey
|
||||
from . import cryptomath
|
||||
|
||||
if cryptomath.m2cryptoLoaded:
|
||||
from .OpenSSL_RSAKey import OpenSSL_RSAKey
|
||||
|
||||
if cryptomath.pycryptoLoaded:
|
||||
from .PyCrypto_RSAKey import PyCrypto_RSAKey
|
||||
|
||||
# **************************************************************************
|
||||
# Factory Functions for RSA Keys
|
||||
# **************************************************************************
|
||||
|
||||
def generateRSAKey(bits, implementations=["openssl", "python"]):
|
||||
"""Generate an RSA key with the specified bit length.
|
||||
|
||||
@type bits: int
|
||||
@param bits: Desired bit length of the new key's modulus.
|
||||
|
||||
@rtype: L{tlslite.utils.RSAKey.RSAKey}
|
||||
@return: A new RSA private key.
|
||||
"""
|
||||
for implementation in implementations:
|
||||
if implementation == "openssl" and cryptomath.m2cryptoLoaded:
|
||||
return OpenSSL_RSAKey.generate(bits)
|
||||
elif implementation == "python":
|
||||
return Python_RSAKey.generate(bits)
|
||||
raise ValueError("No acceptable implementations")
|
||||
|
||||
def parseXMLKey(s, private=False, public=False, implementations=["python"]):
|
||||
"""Parse an XML-format key.
|
||||
|
||||
The XML format used here is specific to tlslite and cryptoIDlib. The
|
||||
format can store the public component of a key, or the public and
|
||||
private components. For example::
|
||||
|
||||
<publicKey xmlns="http://trevp.net/rsa">
|
||||
<n>4a5yzB8oGNlHo866CAspAC47M4Fvx58zwK8pou...
|
||||
<e>Aw==</e>
|
||||
</publicKey>
|
||||
|
||||
<privateKey xmlns="http://trevp.net/rsa">
|
||||
<n>4a5yzB8oGNlHo866CAspAC47M4Fvx58zwK8pou...
|
||||
<e>Aw==</e>
|
||||
<d>JZ0TIgUxWXmL8KJ0VqyG1V0J3ern9pqIoB0xmy...
|
||||
<p>5PreIj6z6ldIGL1V4+1C36dQFHNCQHJvW52GXc...
|
||||
<q>/E/wDit8YXPCxx126zTq2ilQ3IcW54NJYyNjiZ...
|
||||
<dP>mKc+wX8inDowEH45Qp4slRo1YveBgExKPROu6...
|
||||
<dQ>qDVKtBz9lk0shL5PR3ickXDgkwS576zbl2ztB...
|
||||
<qInv>j6E8EA7dNsTImaXexAmLA1DoeArsYeFAInr...
|
||||
</privateKey>
|
||||
|
||||
@type s: str
|
||||
@param s: A string containing an XML public or private key.
|
||||
|
||||
@type private: bool
|
||||
@param private: If True, a L{SyntaxError} will be raised if the private
|
||||
key component is not present.
|
||||
|
||||
@type public: bool
|
||||
@param public: If True, the private key component (if present) will be
|
||||
discarded, so this function will always return a public key.
|
||||
|
||||
@rtype: L{tlslite.utils.RSAKey.RSAKey}
|
||||
@return: An RSA key.
|
||||
|
||||
@raise SyntaxError: If the key is not properly formatted.
|
||||
"""
|
||||
for implementation in implementations:
|
||||
if implementation == "python":
|
||||
key = Python_RSAKey.parseXML(s)
|
||||
break
|
||||
else:
|
||||
raise ValueError("No acceptable implementations")
|
||||
|
||||
return _parseKeyHelper(key, private, public)
|
||||
|
||||
#Parse as an OpenSSL or Python key
|
||||
def parsePEMKey(s, private=False, public=False, passwordCallback=None,
|
||||
implementations=["openssl", "python"]):
|
||||
"""Parse a PEM-format key.
|
||||
|
||||
The PEM format is used by OpenSSL and other tools. The
|
||||
format is typically used to store both the public and private
|
||||
components of a key. For example::
|
||||
|
||||
-----BEGIN RSA PRIVATE KEY-----
|
||||
MIICXQIBAAKBgQDYscuoMzsGmW0pAYsmyHltxB2TdwHS0dImfjCMfaSDkfLdZY5+
|
||||
dOWORVns9etWnr194mSGA1F0Pls/VJW8+cX9+3vtJV8zSdANPYUoQf0TP7VlJxkH
|
||||
dSRkUbEoz5bAAs/+970uos7n7iXQIni+3erUTdYEk2iWnMBjTljfgbK/dQIDAQAB
|
||||
AoGAJHoJZk75aKr7DSQNYIHuruOMdv5ZeDuJvKERWxTrVJqE32/xBKh42/IgqRrc
|
||||
esBN9ZregRCd7YtxoL+EVUNWaJNVx2mNmezEznrc9zhcYUrgeaVdFO2yBF1889zO
|
||||
gCOVwrO8uDgeyj6IKa25H6c1N13ih/o7ZzEgWbGG+ylU1yECQQDv4ZSJ4EjSh/Fl
|
||||
aHdz3wbBa/HKGTjC8iRy476Cyg2Fm8MZUe9Yy3udOrb5ZnS2MTpIXt5AF3h2TfYV
|
||||
VoFXIorjAkEA50FcJmzT8sNMrPaV8vn+9W2Lu4U7C+K/O2g1iXMaZms5PC5zV5aV
|
||||
CKXZWUX1fq2RaOzlbQrpgiolhXpeh8FjxwJBAOFHzSQfSsTNfttp3KUpU0LbiVvv
|
||||
i+spVSnA0O4rq79KpVNmK44Mq67hsW1P11QzrzTAQ6GVaUBRv0YS061td1kCQHnP
|
||||
wtN2tboFR6lABkJDjxoGRvlSt4SOPr7zKGgrWjeiuTZLHXSAnCY+/hr5L9Q3ZwXG
|
||||
6x6iBdgLjVIe4BZQNtcCQQDXGv/gWinCNTN3MPWfTW/RGzuMYVmyBFais0/VrgdH
|
||||
h1dLpztmpQqfyH/zrBXQ9qL/zR4ojS6XYneO/U18WpEe
|
||||
-----END RSA PRIVATE KEY-----
|
||||
|
||||
To generate a key like this with OpenSSL, run::
|
||||
|
||||
openssl genrsa 2048 > key.pem
|
||||
|
||||
This format also supports password-encrypted private keys. TLS
|
||||
Lite can only handle password-encrypted private keys when OpenSSL
|
||||
and M2Crypto are installed. In this case, passwordCallback will be
|
||||
invoked to query the user for the password.
|
||||
|
||||
@type s: str
|
||||
@param s: A string containing a PEM-encoded public or private key.
|
||||
|
||||
@type private: bool
|
||||
@param private: If True, a L{SyntaxError} will be raised if the
|
||||
private key component is not present.
|
||||
|
||||
@type public: bool
|
||||
@param public: If True, the private key component (if present) will
|
||||
be discarded, so this function will always return a public key.
|
||||
|
||||
@type passwordCallback: callable
|
||||
@param passwordCallback: This function will be called, with no
|
||||
arguments, if the PEM-encoded private key is password-encrypted.
|
||||
The callback should return the password string. If the password is
|
||||
incorrect, SyntaxError will be raised. If no callback is passed
|
||||
and the key is password-encrypted, a prompt will be displayed at
|
||||
the console.
|
||||
|
||||
@rtype: L{tlslite.utils.RSAKey.RSAKey}
|
||||
@return: An RSA key.
|
||||
|
||||
@raise SyntaxError: If the key is not properly formatted.
|
||||
"""
|
||||
for implementation in implementations:
|
||||
if implementation == "openssl" and cryptomath.m2cryptoLoaded:
|
||||
key = OpenSSL_RSAKey.parse(s, passwordCallback)
|
||||
break
|
||||
elif implementation == "python":
|
||||
key = Python_RSAKey.parsePEM(s)
|
||||
break
|
||||
else:
|
||||
raise ValueError("No acceptable implementations")
|
||||
|
||||
return _parseKeyHelper(key, private, public)
|
||||
|
||||
|
||||
def _parseKeyHelper(key, private, public):
|
||||
if private:
|
||||
if not key.hasPrivateKey():
|
||||
raise SyntaxError("Not a private key!")
|
||||
|
||||
if public:
|
||||
return _createPublicKey(key)
|
||||
|
||||
if private:
|
||||
if hasattr(key, "d"):
|
||||
return _createPrivateKey(key)
|
||||
else:
|
||||
return key
|
||||
|
||||
return key
|
||||
|
||||
def parseAsPublicKey(s):
|
||||
"""Parse an XML or PEM-formatted public key.
|
||||
|
||||
@type s: str
|
||||
@param s: A string containing an XML or PEM-encoded public or private key.
|
||||
|
||||
@rtype: L{tlslite.utils.RSAKey.RSAKey}
|
||||
@return: An RSA public key.
|
||||
|
||||
@raise SyntaxError: If the key is not properly formatted.
|
||||
"""
|
||||
try:
|
||||
return parsePEMKey(s, public=True)
|
||||
except:
|
||||
return parseXMLKey(s, public=True)
|
||||
|
||||
def parsePrivateKey(s):
|
||||
"""Parse an XML or PEM-formatted private key.
|
||||
|
||||
@type s: str
|
||||
@param s: A string containing an XML or PEM-encoded private key.
|
||||
|
||||
@rtype: L{tlslite.utils.RSAKey.RSAKey}
|
||||
@return: An RSA private key.
|
||||
|
||||
@raise SyntaxError: If the key is not properly formatted.
|
||||
"""
|
||||
try:
|
||||
return parsePEMKey(s, private=True)
|
||||
except:
|
||||
return parseXMLKey(s, private=True)
|
||||
|
||||
def _createPublicKey(key):
|
||||
"""
|
||||
Create a new public key. Discard any private component,
|
||||
and return the most efficient key possible.
|
||||
"""
|
||||
if not isinstance(key, RSAKey):
|
||||
raise AssertionError()
|
||||
return _createPublicRSAKey(key.n, key.e)
|
||||
|
||||
def _createPrivateKey(key):
|
||||
"""
|
||||
Create a new private key. Return the most efficient key possible.
|
||||
"""
|
||||
if not isinstance(key, RSAKey):
|
||||
raise AssertionError()
|
||||
if not key.hasPrivateKey():
|
||||
raise AssertionError()
|
||||
return _createPrivateRSAKey(key.n, key.e, key.d, key.p, key.q, key.dP,
|
||||
key.dQ, key.qInv)
|
||||
|
||||
def _createPublicRSAKey(n, e, implementations = ["openssl", "pycrypto",
|
||||
"python"]):
|
||||
for implementation in implementations:
|
||||
if implementation == "openssl" and cryptomath.m2cryptoLoaded:
|
||||
return OpenSSL_RSAKey(n, e)
|
||||
elif implementation == "pycrypto" and cryptomath.pycryptoLoaded:
|
||||
return PyCrypto_RSAKey(n, e)
|
||||
elif implementation == "python":
|
||||
return Python_RSAKey(n, e)
|
||||
raise ValueError("No acceptable implementations")
|
||||
|
||||
def _createPrivateRSAKey(n, e, d, p, q, dP, dQ, qInv,
|
||||
implementations = ["pycrypto", "python"]):
|
||||
for implementation in implementations:
|
||||
if implementation == "pycrypto" and cryptomath.pycryptoLoaded:
|
||||
return PyCrypto_RSAKey(n, e, d, p, q, dP, dQ, qInv)
|
||||
elif implementation == "python":
|
||||
return Python_RSAKey(n, e, d, p, q, dP, dQ, qInv)
|
||||
raise ValueError("No acceptable implementations")
|
||||
@@ -1,392 +0,0 @@
|
||||
"""
|
||||
A pure python (slow) implementation of rijndael with a decent interface
|
||||
|
||||
To include -
|
||||
|
||||
from rijndael import rijndael
|
||||
|
||||
To do a key setup -
|
||||
|
||||
r = rijndael(key, block_size = 16)
|
||||
|
||||
key must be a string of length 16, 24, or 32
|
||||
blocksize must be 16, 24, or 32. Default is 16
|
||||
|
||||
To use -
|
||||
|
||||
ciphertext = r.encrypt(plaintext)
|
||||
plaintext = r.decrypt(ciphertext)
|
||||
|
||||
If any strings are of the wrong length a ValueError is thrown
|
||||
"""
|
||||
|
||||
# ported from the Java reference code by Bram Cohen, bram@gawth.com, April 2001
|
||||
# this code is public domain, unless someone makes
|
||||
# an intellectual property claim against the reference
|
||||
# code, in which case it can be made public domain by
|
||||
# deleting all the comments and renaming all the variables
|
||||
|
||||
import copy
|
||||
import string
|
||||
|
||||
|
||||
|
||||
#-----------------------
|
||||
#TREV - ADDED BECAUSE THERE'S WARNINGS ABOUT INT OVERFLOW BEHAVIOR CHANGING IN
|
||||
#2.4.....
|
||||
import os
|
||||
if os.name != "java":
|
||||
import exceptions
|
||||
if hasattr(exceptions, "FutureWarning"):
|
||||
import warnings
|
||||
warnings.filterwarnings("ignore", category=FutureWarning, append=1)
|
||||
#-----------------------
|
||||
|
||||
|
||||
|
||||
shifts = [[[0, 0], [1, 3], [2, 2], [3, 1]],
|
||||
[[0, 0], [1, 5], [2, 4], [3, 3]],
|
||||
[[0, 0], [1, 7], [3, 5], [4, 4]]]
|
||||
|
||||
# [keysize][block_size]
|
||||
num_rounds = {16: {16: 10, 24: 12, 32: 14}, 24: {16: 12, 24: 12, 32: 14}, 32: {16: 14, 24: 14, 32: 14}}
|
||||
|
||||
A = [[1, 1, 1, 1, 1, 0, 0, 0],
|
||||
[0, 1, 1, 1, 1, 1, 0, 0],
|
||||
[0, 0, 1, 1, 1, 1, 1, 0],
|
||||
[0, 0, 0, 1, 1, 1, 1, 1],
|
||||
[1, 0, 0, 0, 1, 1, 1, 1],
|
||||
[1, 1, 0, 0, 0, 1, 1, 1],
|
||||
[1, 1, 1, 0, 0, 0, 1, 1],
|
||||
[1, 1, 1, 1, 0, 0, 0, 1]]
|
||||
|
||||
# produce log and alog tables, needed for multiplying in the
|
||||
# field GF(2^m) (generator = 3)
|
||||
alog = [1]
|
||||
for i in range(255):
|
||||
j = (alog[-1] << 1) ^ alog[-1]
|
||||
if j & 0x100 != 0:
|
||||
j ^= 0x11B
|
||||
alog.append(j)
|
||||
|
||||
log = [0] * 256
|
||||
for i in range(1, 255):
|
||||
log[alog[i]] = i
|
||||
|
||||
# multiply two elements of GF(2^m)
|
||||
def mul(a, b):
|
||||
if a == 0 or b == 0:
|
||||
return 0
|
||||
return alog[(log[a & 0xFF] + log[b & 0xFF]) % 255]
|
||||
|
||||
# substitution box based on F^{-1}(x)
|
||||
box = [[0] * 8 for i in range(256)]
|
||||
box[1][7] = 1
|
||||
for i in range(2, 256):
|
||||
j = alog[255 - log[i]]
|
||||
for t in range(8):
|
||||
box[i][t] = (j >> (7 - t)) & 0x01
|
||||
|
||||
B = [0, 1, 1, 0, 0, 0, 1, 1]
|
||||
|
||||
# affine transform: box[i] <- B + A*box[i]
|
||||
cox = [[0] * 8 for i in range(256)]
|
||||
for i in range(256):
|
||||
for t in range(8):
|
||||
cox[i][t] = B[t]
|
||||
for j in range(8):
|
||||
cox[i][t] ^= A[t][j] * box[i][j]
|
||||
|
||||
# S-boxes and inverse S-boxes
|
||||
S = [0] * 256
|
||||
Si = [0] * 256
|
||||
for i in range(256):
|
||||
S[i] = cox[i][0] << 7
|
||||
for t in range(1, 8):
|
||||
S[i] ^= cox[i][t] << (7-t)
|
||||
Si[S[i] & 0xFF] = i
|
||||
|
||||
# T-boxes
|
||||
G = [[2, 1, 1, 3],
|
||||
[3, 2, 1, 1],
|
||||
[1, 3, 2, 1],
|
||||
[1, 1, 3, 2]]
|
||||
|
||||
AA = [[0] * 8 for i in range(4)]
|
||||
|
||||
for i in range(4):
|
||||
for j in range(4):
|
||||
AA[i][j] = G[i][j]
|
||||
AA[i][i+4] = 1
|
||||
|
||||
for i in range(4):
|
||||
pivot = AA[i][i]
|
||||
if pivot == 0:
|
||||
t = i + 1
|
||||
while AA[t][i] == 0 and t < 4:
|
||||
t += 1
|
||||
assert t != 4, 'G matrix must be invertible'
|
||||
for j in range(8):
|
||||
AA[i][j], AA[t][j] = AA[t][j], AA[i][j]
|
||||
pivot = AA[i][i]
|
||||
for j in range(8):
|
||||
if AA[i][j] != 0:
|
||||
AA[i][j] = alog[(255 + log[AA[i][j] & 0xFF] - log[pivot & 0xFF]) % 255]
|
||||
for t in range(4):
|
||||
if i != t:
|
||||
for j in range(i+1, 8):
|
||||
AA[t][j] ^= mul(AA[i][j], AA[t][i])
|
||||
AA[t][i] = 0
|
||||
|
||||
iG = [[0] * 4 for i in range(4)]
|
||||
|
||||
for i in range(4):
|
||||
for j in range(4):
|
||||
iG[i][j] = AA[i][j + 4]
|
||||
|
||||
def mul4(a, bs):
|
||||
if a == 0:
|
||||
return 0
|
||||
r = 0
|
||||
for b in bs:
|
||||
r <<= 8
|
||||
if b != 0:
|
||||
r = r | mul(a, b)
|
||||
return r
|
||||
|
||||
T1 = []
|
||||
T2 = []
|
||||
T3 = []
|
||||
T4 = []
|
||||
T5 = []
|
||||
T6 = []
|
||||
T7 = []
|
||||
T8 = []
|
||||
U1 = []
|
||||
U2 = []
|
||||
U3 = []
|
||||
U4 = []
|
||||
|
||||
for t in range(256):
|
||||
s = S[t]
|
||||
T1.append(mul4(s, G[0]))
|
||||
T2.append(mul4(s, G[1]))
|
||||
T3.append(mul4(s, G[2]))
|
||||
T4.append(mul4(s, G[3]))
|
||||
|
||||
s = Si[t]
|
||||
T5.append(mul4(s, iG[0]))
|
||||
T6.append(mul4(s, iG[1]))
|
||||
T7.append(mul4(s, iG[2]))
|
||||
T8.append(mul4(s, iG[3]))
|
||||
|
||||
U1.append(mul4(t, iG[0]))
|
||||
U2.append(mul4(t, iG[1]))
|
||||
U3.append(mul4(t, iG[2]))
|
||||
U4.append(mul4(t, iG[3]))
|
||||
|
||||
# round constants
|
||||
rcon = [1]
|
||||
r = 1
|
||||
for t in range(1, 30):
|
||||
r = mul(2, r)
|
||||
rcon.append(r)
|
||||
|
||||
del A
|
||||
del AA
|
||||
del pivot
|
||||
del B
|
||||
del G
|
||||
del box
|
||||
del log
|
||||
del alog
|
||||
del i
|
||||
del j
|
||||
del r
|
||||
del s
|
||||
del t
|
||||
del mul
|
||||
del mul4
|
||||
del cox
|
||||
del iG
|
||||
|
||||
class rijndael:
|
||||
def __init__(self, key, block_size = 16):
|
||||
if block_size != 16 and block_size != 24 and block_size != 32:
|
||||
raise ValueError('Invalid block size: ' + str(block_size))
|
||||
if len(key) != 16 and len(key) != 24 and len(key) != 32:
|
||||
raise ValueError('Invalid key size: ' + str(len(key)))
|
||||
self.block_size = block_size
|
||||
|
||||
ROUNDS = num_rounds[len(key)][block_size]
|
||||
BC = block_size / 4
|
||||
# encryption round keys
|
||||
Ke = [[0] * BC for i in range(ROUNDS + 1)]
|
||||
# decryption round keys
|
||||
Kd = [[0] * BC for i in range(ROUNDS + 1)]
|
||||
ROUND_KEY_COUNT = (ROUNDS + 1) * BC
|
||||
KC = len(key) / 4
|
||||
|
||||
# copy user material bytes into temporary ints
|
||||
tk = []
|
||||
for i in range(0, KC):
|
||||
tk.append((ord(key[i * 4]) << 24) | (ord(key[i * 4 + 1]) << 16) |
|
||||
(ord(key[i * 4 + 2]) << 8) | ord(key[i * 4 + 3]))
|
||||
|
||||
# copy values into round key arrays
|
||||
t = 0
|
||||
j = 0
|
||||
while j < KC and t < ROUND_KEY_COUNT:
|
||||
Ke[t / BC][t % BC] = tk[j]
|
||||
Kd[ROUNDS - (t / BC)][t % BC] = tk[j]
|
||||
j += 1
|
||||
t += 1
|
||||
tt = 0
|
||||
rconpointer = 0
|
||||
while t < ROUND_KEY_COUNT:
|
||||
# extrapolate using phi (the round key evolution function)
|
||||
tt = tk[KC - 1]
|
||||
tk[0] ^= (S[(tt >> 16) & 0xFF] & 0xFF) << 24 ^ \
|
||||
(S[(tt >> 8) & 0xFF] & 0xFF) << 16 ^ \
|
||||
(S[ tt & 0xFF] & 0xFF) << 8 ^ \
|
||||
(S[(tt >> 24) & 0xFF] & 0xFF) ^ \
|
||||
(rcon[rconpointer] & 0xFF) << 24
|
||||
rconpointer += 1
|
||||
if KC != 8:
|
||||
for i in range(1, KC):
|
||||
tk[i] ^= tk[i-1]
|
||||
else:
|
||||
for i in range(1, KC / 2):
|
||||
tk[i] ^= tk[i-1]
|
||||
tt = tk[KC / 2 - 1]
|
||||
tk[KC / 2] ^= (S[ tt & 0xFF] & 0xFF) ^ \
|
||||
(S[(tt >> 8) & 0xFF] & 0xFF) << 8 ^ \
|
||||
(S[(tt >> 16) & 0xFF] & 0xFF) << 16 ^ \
|
||||
(S[(tt >> 24) & 0xFF] & 0xFF) << 24
|
||||
for i in range(KC / 2 + 1, KC):
|
||||
tk[i] ^= tk[i-1]
|
||||
# copy values into round key arrays
|
||||
j = 0
|
||||
while j < KC and t < ROUND_KEY_COUNT:
|
||||
Ke[t / BC][t % BC] = tk[j]
|
||||
Kd[ROUNDS - (t / BC)][t % BC] = tk[j]
|
||||
j += 1
|
||||
t += 1
|
||||
# inverse MixColumn where needed
|
||||
for r in range(1, ROUNDS):
|
||||
for j in range(BC):
|
||||
tt = Kd[r][j]
|
||||
Kd[r][j] = U1[(tt >> 24) & 0xFF] ^ \
|
||||
U2[(tt >> 16) & 0xFF] ^ \
|
||||
U3[(tt >> 8) & 0xFF] ^ \
|
||||
U4[ tt & 0xFF]
|
||||
self.Ke = Ke
|
||||
self.Kd = Kd
|
||||
|
||||
def encrypt(self, plaintext):
|
||||
if len(plaintext) != self.block_size:
|
||||
raise ValueError('wrong block length, expected ' + str(self.block_size) + ' got ' + str(len(plaintext)))
|
||||
Ke = self.Ke
|
||||
|
||||
BC = self.block_size / 4
|
||||
ROUNDS = len(Ke) - 1
|
||||
if BC == 4:
|
||||
SC = 0
|
||||
elif BC == 6:
|
||||
SC = 1
|
||||
else:
|
||||
SC = 2
|
||||
s1 = shifts[SC][1][0]
|
||||
s2 = shifts[SC][2][0]
|
||||
s3 = shifts[SC][3][0]
|
||||
a = [0] * BC
|
||||
# temporary work array
|
||||
t = []
|
||||
# plaintext to ints + key
|
||||
for i in range(BC):
|
||||
t.append((ord(plaintext[i * 4 ]) << 24 |
|
||||
ord(plaintext[i * 4 + 1]) << 16 |
|
||||
ord(plaintext[i * 4 + 2]) << 8 |
|
||||
ord(plaintext[i * 4 + 3]) ) ^ Ke[0][i])
|
||||
# apply round transforms
|
||||
for r in range(1, ROUNDS):
|
||||
for i in range(BC):
|
||||
a[i] = (T1[(t[ i ] >> 24) & 0xFF] ^
|
||||
T2[(t[(i + s1) % BC] >> 16) & 0xFF] ^
|
||||
T3[(t[(i + s2) % BC] >> 8) & 0xFF] ^
|
||||
T4[ t[(i + s3) % BC] & 0xFF] ) ^ Ke[r][i]
|
||||
t = copy.copy(a)
|
||||
# last round is special
|
||||
result = []
|
||||
for i in range(BC):
|
||||
tt = Ke[ROUNDS][i]
|
||||
result.append((S[(t[ i ] >> 24) & 0xFF] ^ (tt >> 24)) & 0xFF)
|
||||
result.append((S[(t[(i + s1) % BC] >> 16) & 0xFF] ^ (tt >> 16)) & 0xFF)
|
||||
result.append((S[(t[(i + s2) % BC] >> 8) & 0xFF] ^ (tt >> 8)) & 0xFF)
|
||||
result.append((S[ t[(i + s3) % BC] & 0xFF] ^ tt ) & 0xFF)
|
||||
return string.join(list(map(chr, result)), '')
|
||||
|
||||
def decrypt(self, ciphertext):
|
||||
if len(ciphertext) != self.block_size:
|
||||
raise ValueError('wrong block length, expected ' + str(self.block_size) + ' got ' + str(len(plaintext)))
|
||||
Kd = self.Kd
|
||||
|
||||
BC = self.block_size / 4
|
||||
ROUNDS = len(Kd) - 1
|
||||
if BC == 4:
|
||||
SC = 0
|
||||
elif BC == 6:
|
||||
SC = 1
|
||||
else:
|
||||
SC = 2
|
||||
s1 = shifts[SC][1][1]
|
||||
s2 = shifts[SC][2][1]
|
||||
s3 = shifts[SC][3][1]
|
||||
a = [0] * BC
|
||||
# temporary work array
|
||||
t = [0] * BC
|
||||
# ciphertext to ints + key
|
||||
for i in range(BC):
|
||||
t[i] = (ord(ciphertext[i * 4 ]) << 24 |
|
||||
ord(ciphertext[i * 4 + 1]) << 16 |
|
||||
ord(ciphertext[i * 4 + 2]) << 8 |
|
||||
ord(ciphertext[i * 4 + 3]) ) ^ Kd[0][i]
|
||||
# apply round transforms
|
||||
for r in range(1, ROUNDS):
|
||||
for i in range(BC):
|
||||
a[i] = (T5[(t[ i ] >> 24) & 0xFF] ^
|
||||
T6[(t[(i + s1) % BC] >> 16) & 0xFF] ^
|
||||
T7[(t[(i + s2) % BC] >> 8) & 0xFF] ^
|
||||
T8[ t[(i + s3) % BC] & 0xFF] ) ^ Kd[r][i]
|
||||
t = copy.copy(a)
|
||||
# last round is special
|
||||
result = []
|
||||
for i in range(BC):
|
||||
tt = Kd[ROUNDS][i]
|
||||
result.append((Si[(t[ i ] >> 24) & 0xFF] ^ (tt >> 24)) & 0xFF)
|
||||
result.append((Si[(t[(i + s1) % BC] >> 16) & 0xFF] ^ (tt >> 16)) & 0xFF)
|
||||
result.append((Si[(t[(i + s2) % BC] >> 8) & 0xFF] ^ (tt >> 8)) & 0xFF)
|
||||
result.append((Si[ t[(i + s3) % BC] & 0xFF] ^ tt ) & 0xFF)
|
||||
return string.join(list(map(chr, result)), '')
|
||||
|
||||
def encrypt(key, block):
|
||||
return rijndael(key, len(block)).encrypt(block)
|
||||
|
||||
def decrypt(key, block):
|
||||
return rijndael(key, len(block)).decrypt(block)
|
||||
|
||||
def test():
|
||||
def t(kl, bl):
|
||||
b = 'b' * bl
|
||||
r = rijndael('a' * kl, bl)
|
||||
assert r.decrypt(r.encrypt(b)) == b
|
||||
t(16, 16)
|
||||
t(16, 24)
|
||||
t(16, 32)
|
||||
t(24, 16)
|
||||
t(24, 24)
|
||||
t(24, 32)
|
||||
t(32, 16)
|
||||
t(32, 24)
|
||||
t(32, 32)
|
||||
|
||||
@@ -1,63 +0,0 @@
|
||||
|
||||
#include "Python.h"
|
||||
#define _WIN32_WINNT 0x0400 /* Needed for CryptoAPI on some systems */
|
||||
#include <windows.h>
|
||||
|
||||
|
||||
static PyObject* getRandomBytes(PyObject *self, PyObject *args)
|
||||
{
|
||||
int howMany;
|
||||
HCRYPTPROV hCryptProv;
|
||||
unsigned char* bytes = NULL;
|
||||
PyObject* returnVal = NULL;
|
||||
|
||||
|
||||
/* Read Arguments */
|
||||
if (!PyArg_ParseTuple(args, "i", &howMany))
|
||||
return(NULL);
|
||||
|
||||
/* Get Context */
|
||||
if(CryptAcquireContext(
|
||||
&hCryptProv,
|
||||
NULL,
|
||||
NULL,
|
||||
PROV_RSA_FULL,
|
||||
CRYPT_VERIFYCONTEXT) == 0)
|
||||
return Py_BuildValue("s#", NULL, 0);
|
||||
|
||||
|
||||
/* Allocate bytes */
|
||||
bytes = malloc(howMany);
|
||||
|
||||
|
||||
/* Get random data */
|
||||
if(CryptGenRandom(
|
||||
hCryptProv,
|
||||
howMany,
|
||||
bytes) == 0)
|
||||
returnVal = Py_BuildValue("s#", NULL, 0);
|
||||
else
|
||||
returnVal = Py_BuildValue("s#", bytes, howMany);
|
||||
|
||||
free(bytes);
|
||||
CryptReleaseContext(hCryptProv, 0);
|
||||
|
||||
return returnVal;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* List of functions exported by this module */
|
||||
|
||||
static struct PyMethodDef win32prng_functions[] = {
|
||||
{"getRandomBytes", (PyCFunction)getRandomBytes, METH_VARARGS},
|
||||
{NULL, NULL} /* Sentinel */
|
||||
};
|
||||
|
||||
|
||||
/* Initialize this module. */
|
||||
|
||||
DL_EXPORT(void) initwin32prng(void)
|
||||
{
|
||||
Py_InitModule("win32prng", win32prng_functions);
|
||||
}
|
||||
@@ -1,202 +0,0 @@
|
||||
"""Helper functions for XML.
|
||||
|
||||
This module has misc. helper functions for working with XML DOM nodes."""
|
||||
|
||||
from .compat import *
|
||||
import os
|
||||
import re
|
||||
|
||||
if os.name == "java":
|
||||
# Only for Jython
|
||||
from javax.xml.parsers import *
|
||||
import java
|
||||
|
||||
builder = DocumentBuilderFactory.newInstance().newDocumentBuilder()
|
||||
|
||||
def parseDocument(s):
|
||||
stream = java.io.ByteArrayInputStream(java.lang.String(s).getBytes())
|
||||
return builder.parse(stream)
|
||||
else:
|
||||
from xml.dom import minidom
|
||||
from xml.sax import saxutils
|
||||
|
||||
def parseDocument(s):
|
||||
return minidom.parseString(s)
|
||||
|
||||
def parseAndStripWhitespace(s):
|
||||
try:
|
||||
element = parseDocument(s).documentElement
|
||||
except BaseException as e:
|
||||
raise SyntaxError(str(e))
|
||||
stripWhitespace(element)
|
||||
return element
|
||||
|
||||
#Goes through a DOM tree and removes whitespace besides child elements,
|
||||
#as long as this whitespace is correctly tab-ified
|
||||
def stripWhitespace(element, tab=0):
|
||||
element.normalize()
|
||||
|
||||
lastSpacer = "\n" + ("\t"*tab)
|
||||
spacer = lastSpacer + "\t"
|
||||
|
||||
#Zero children aren't allowed (i.e. <empty/>)
|
||||
#This makes writing output simpler, and matches Canonical XML
|
||||
if element.childNodes.length==0: #DON'T DO len(element.childNodes) - doesn't work in Jython
|
||||
raise SyntaxError("Empty XML elements not allowed")
|
||||
|
||||
#If there's a single child, it must be text context
|
||||
if element.childNodes.length==1:
|
||||
if element.firstChild.nodeType == element.firstChild.TEXT_NODE:
|
||||
#If it's an empty element, remove
|
||||
if element.firstChild.data == lastSpacer:
|
||||
element.removeChild(element.firstChild)
|
||||
return
|
||||
#If not text content, give an error
|
||||
elif element.firstChild.nodeType == element.firstChild.ELEMENT_NODE:
|
||||
raise SyntaxError("Bad whitespace under '%s'" % element.tagName)
|
||||
else:
|
||||
raise SyntaxError("Unexpected node type in XML document")
|
||||
|
||||
#Otherwise there's multiple child element
|
||||
child = element.firstChild
|
||||
while child:
|
||||
if child.nodeType == child.ELEMENT_NODE:
|
||||
stripWhitespace(child, tab+1)
|
||||
child = child.nextSibling
|
||||
elif child.nodeType == child.TEXT_NODE:
|
||||
if child == element.lastChild:
|
||||
if child.data != lastSpacer:
|
||||
raise SyntaxError("Bad whitespace under '%s'" % element.tagName)
|
||||
elif child.data != spacer:
|
||||
raise SyntaxError("Bad whitespace under '%s'" % element.tagName)
|
||||
next = child.nextSibling
|
||||
element.removeChild(child)
|
||||
child = next
|
||||
else:
|
||||
raise SyntaxError("Unexpected node type in XML document")
|
||||
|
||||
|
||||
def checkName(element, name):
|
||||
if element.nodeType != element.ELEMENT_NODE:
|
||||
raise SyntaxError("Missing element: '%s'" % name)
|
||||
|
||||
if name == None:
|
||||
return
|
||||
|
||||
if element.tagName != name:
|
||||
raise SyntaxError("Wrong element name: should be '%s', is '%s'" % (name, element.tagName))
|
||||
|
||||
def getChild(element, index, name=None):
|
||||
if element.nodeType != element.ELEMENT_NODE:
|
||||
raise SyntaxError("Wrong node type in getChild()")
|
||||
|
||||
child = element.childNodes.item(index)
|
||||
if child == None:
|
||||
raise SyntaxError("Missing child: '%s'" % name)
|
||||
checkName(child, name)
|
||||
return child
|
||||
|
||||
def getChildIter(element, index):
|
||||
class ChildIter:
|
||||
def __init__(self, element, index):
|
||||
self.element = element
|
||||
self.index = index
|
||||
|
||||
def __next__(self):
|
||||
if self.index < len(self.element.childNodes):
|
||||
retVal = self.element.childNodes.item(self.index)
|
||||
self.index += 1
|
||||
else:
|
||||
retVal = None
|
||||
return retVal
|
||||
|
||||
def checkEnd(self):
|
||||
if self.index != len(self.element.childNodes):
|
||||
raise SyntaxError("Too many elements under: '%s'" % self.element.tagName)
|
||||
return ChildIter(element, index)
|
||||
|
||||
def getChildOrNone(element, index):
|
||||
if element.nodeType != element.ELEMENT_NODE:
|
||||
raise SyntaxError("Wrong node type in getChild()")
|
||||
child = element.childNodes.item(index)
|
||||
return child
|
||||
|
||||
def getLastChild(element, index, name=None):
|
||||
if element.nodeType != element.ELEMENT_NODE:
|
||||
raise SyntaxError("Wrong node type in getLastChild()")
|
||||
|
||||
child = element.childNodes.item(index)
|
||||
if child == None:
|
||||
raise SyntaxError("Missing child: '%s'" % name)
|
||||
if child != element.lastChild:
|
||||
raise SyntaxError("Too many elements under: '%s'" % element.tagName)
|
||||
checkName(child, name)
|
||||
return child
|
||||
|
||||
#Regular expressions for syntax-checking attribute and element content
|
||||
nsRegEx = "http://trevp.net/cryptoID\Z"
|
||||
cryptoIDRegEx = "([a-km-z3-9]{5}\.){3}[a-km-z3-9]{5}\Z"
|
||||
urlRegEx = "http(s)?://.{1,100}\Z"
|
||||
sha1Base64RegEx = "[A-Za-z0-9+/]{27}=\Z"
|
||||
base64RegEx = "[A-Za-z0-9+/]+={0,4}\Z"
|
||||
certsListRegEx = "(0)?(1)?(2)?(3)?(4)?(5)?(6)?(7)?(8)?(9)?\Z"
|
||||
keyRegEx = "[A-Z]\Z"
|
||||
keysListRegEx = "(A)?(B)?(C)?(D)?(E)?(F)?(G)?(H)?(I)?(J)?(K)?(L)?(M)?(N)?(O)?(P)?(Q)?(R)?(S)?(T)?(U)?(V)?(W)?(X)?(Y)?(Z)?\Z"
|
||||
dateTimeRegEx = "\d\d\d\d-\d\d-\d\dT\d\d:\d\d:\d\dZ\Z"
|
||||
shortStringRegEx = ".{1,100}\Z"
|
||||
exprRegEx = "[a-zA-Z0-9 ,()]{1,200}\Z"
|
||||
notAfterDeltaRegEx = "0|([1-9][0-9]{0,8})\Z" #A number from 0 to (1 billion)-1
|
||||
booleanRegEx = "(true)|(false)"
|
||||
|
||||
def getReqAttribute(element, attrName, regEx=""):
|
||||
if element.nodeType != element.ELEMENT_NODE:
|
||||
raise SyntaxError("Wrong node type in getReqAttribute()")
|
||||
|
||||
value = element.getAttribute(attrName)
|
||||
if not value:
|
||||
raise SyntaxError("Missing Attribute: " + attrName)
|
||||
if not re.match(regEx, value):
|
||||
raise SyntaxError("Bad Attribute Value for '%s': '%s' " % (attrName, value))
|
||||
element.removeAttribute(attrName)
|
||||
return str(value) #de-unicode it; this is needed for bsddb, for example
|
||||
|
||||
def getAttribute(element, attrName, regEx=""):
|
||||
if element.nodeType != element.ELEMENT_NODE:
|
||||
raise SyntaxError("Wrong node type in getAttribute()")
|
||||
|
||||
value = element.getAttribute(attrName)
|
||||
if value:
|
||||
if not re.match(regEx, value):
|
||||
raise SyntaxError("Bad Attribute Value for '%s': '%s' " % (attrName, value))
|
||||
element.removeAttribute(attrName)
|
||||
return str(value) #de-unicode it; this is needed for bsddb, for example
|
||||
|
||||
def checkNoMoreAttributes(element):
|
||||
if element.nodeType != element.ELEMENT_NODE:
|
||||
raise SyntaxError("Wrong node type in checkNoMoreAttributes()")
|
||||
|
||||
if element.attributes.length!=0:
|
||||
raise SyntaxError("Extra attributes on '%s'" % element.tagName)
|
||||
|
||||
def getText(element, regEx=""):
|
||||
textNode = element.firstChild
|
||||
if textNode == None:
|
||||
raise SyntaxError("Empty element '%s'" % element.tagName)
|
||||
if textNode.nodeType != textNode.TEXT_NODE:
|
||||
raise SyntaxError("Non-text node: '%s'" % element.tagName)
|
||||
if not re.match(regEx, textNode.data):
|
||||
raise SyntaxError("Bad Text Value for '%s': '%s' " % (element.tagName, textNode.data))
|
||||
return str(textNode.data) #de-unicode it; this is needed for bsddb, for example
|
||||
|
||||
#Function for adding tabs to a string
|
||||
def indent(s, steps, ch="\t"):
|
||||
tabs = ch*steps
|
||||
if s[-1] != "\n":
|
||||
s = tabs + s.replace("\n", "\n"+tabs)
|
||||
else:
|
||||
s = tabs + s.replace("\n", "\n"+tabs)
|
||||
s = s[ : -len(tabs)]
|
||||
return s
|
||||
|
||||
def escape(s):
|
||||
return saxutils.escape(s)
|
||||
Reference in New Issue
Block a user