basic functionalty
This commit is contained in:
commit
ee5db19d8e
191
main.py
Normal file
191
main.py
Normal file
@ -0,0 +1,191 @@
|
||||
import ScreenCloud
|
||||
import time
|
||||
from PythonQt.QtCore import QFile, QSettings
|
||||
from PythonQt.QtGui import QDesktopServices, QMessageBox
|
||||
from PythonQt.QtUiTools import QUiLoader
|
||||
|
||||
from seafapi import *
|
||||
###############################
|
||||
## This is a temporary fix, should be removed when a newer python version is used ##
|
||||
import logging
|
||||
logging.captureWarnings(True)
|
||||
###############################
|
||||
|
||||
class SeafileUploader():
|
||||
def __init__(self):
|
||||
self.uil = QUiLoader()
|
||||
self.loadSettings()
|
||||
self.seaf_lib = None
|
||||
self.libs = None
|
||||
|
||||
def showSettingsUI(self, parentWidget):
|
||||
self.parentWidget = parentWidget
|
||||
self.settingsDialog = self.uil.load(QFile(workingDir + "/settings.ui"), parentWidget)
|
||||
self.settingsDialog.group_account.widget_loggedIn.loginButton.connect("clicked()", self.startAuthenticationProcess)
|
||||
#self.settingsDialog.group_name.input_name.connect("textChanged(QString)", self.nameFormatEdited)
|
||||
self.settingsDialog.group_location.widget_location.pathEdit.connect("textChanged(QString)", self.locationUpdate)
|
||||
self.settingsDialog.group_location.widget_location.libraryEditDrop.connect("currentIndexChanged(QString)", self.locationUpdate)
|
||||
self.settingsDialog.connect("accepted()", self.saveSettings)
|
||||
|
||||
self.loadSettings()
|
||||
self.updateUi()
|
||||
self.settingsDialog.open()
|
||||
|
||||
def populateLibrarySelector(self):
|
||||
drop = self.settingsDialog.group_location.widget_location.libraryEditDrop
|
||||
drop.clear()
|
||||
drop.setEnabled(False)
|
||||
if self.isConfigured():
|
||||
self.libs = (self.seaf_client or self.seaf_lib.client).libraries()
|
||||
i=0
|
||||
select = 0
|
||||
for lib in self.libs:
|
||||
print "%s %s <-> %s%s" % (lib.name,lib.id,self.seaf_lib.name,self.seaf_lib.id)
|
||||
if self.seaf_lib is not None and lib.id == self.seaf_lib.id:
|
||||
select = i
|
||||
print "set %s" % lib.name
|
||||
drop.addItem(lib.name)
|
||||
i=i+1
|
||||
drop.setCurrentIndex(select)
|
||||
drop.setEnabled(True)
|
||||
|
||||
def updateUi(self):
|
||||
self.settingsDialog.group_account.widget_loggedIn.usernameEdit.setText(self.access_token.username if not (self.access_token is None) else "")
|
||||
self.settingsDialog.group_account.widget_loggedIn.serverUrlEdit.setText(self.seaf_url or "https://seaf.shimun.net")
|
||||
self.settingsDialog.group_account.widget_loggedIn.loginButton.setText("Logout" if self.access_token is not None else "Login")
|
||||
#
|
||||
if self.access_token is None and len(self.settingsDialog.group_account.widget_loggedIn.passwordEdit.text) > 0:
|
||||
self.settingsDialog.group_account.widget_loggedIn.passwordEdit.setText("X" * 8)
|
||||
for elm in [self.settingsDialog.group_account.widget_loggedIn.usernameEdit, self.settingsDialog.group_account.widget_loggedIn.serverUrlEdit, self.settingsDialog.group_account.widget_loggedIn.passwordEdit]:
|
||||
elm.setEnabled(self.access_token is None)
|
||||
self.settingsDialog.group_location.setEnabled(self.access_token is not None)
|
||||
self.settingsDialog.group_location.widget_location.pathEdit.setText(self.lib_path or "/")
|
||||
self.settingsDialog.linkCopyCheck.setChecked(self.copyLink)
|
||||
self.populateLibrarySelector()
|
||||
self.settingsDialog.adjustSize()
|
||||
|
||||
def loadSettings(self):
|
||||
settings = QSettings()
|
||||
settings.beginGroup("uploaders")
|
||||
settings.beginGroup("seafile")
|
||||
self.seaf_url = settings.value("seafile-url", "")
|
||||
(self.lib_id,self.lib_name) = str(settings.value("library", "/")).split("/")
|
||||
self.lib_path = settings.value("library-path", "")
|
||||
self.copyLink = settings.value("copy-link", "true") in ['true', True]
|
||||
if settings.value("auth-token", False) and settings.value("auth-username", False):
|
||||
self.access_token = SeafileToken(settings.value("auth-username", False),settings.value("auth-token", False))
|
||||
else:
|
||||
self.access_token = None
|
||||
self.nameFormat = settings.value("name-format", "Screenshot at %H:%M:%S")
|
||||
settings.endGroup()
|
||||
settings.endGroup()
|
||||
if self.isConfigured():
|
||||
self.seaf_client = SeafileClient(self.seaf_url,self.access_token.username,token=self.access_token)
|
||||
for lib in self.seaf_client.libraries():
|
||||
if lib.id == self.lib_id:
|
||||
self.seaf_lib = lib
|
||||
|
||||
def saveSettings(self):
|
||||
settings = QSettings()
|
||||
settings.beginGroup("uploaders")
|
||||
settings.beginGroup("seafile")
|
||||
self.copyLink = self.settingsDialog.linkCopyCheck.checked
|
||||
settings.setValue("copy-link", self.copyLink)
|
||||
if self.access_token is not None:
|
||||
settings.setValue("auth-username", self.access_token.username )
|
||||
settings.setValue("auth-token", self.access_token.token)
|
||||
settings.setValue("seafile-url", self.seaf_url)
|
||||
if self.seaf_lib is not None:
|
||||
settings.setValue("library", "%s/%s" % (self.seaf_lib.id,self.seaf_lib.name))
|
||||
settings.setValue("library-path", self.lib_path)
|
||||
#settings.setValue("name-format", self.settingsDialog.group_name.input_name.text)
|
||||
print self.seaf_lib, self.lib_path
|
||||
settings.endGroup()
|
||||
settings.endGroup()
|
||||
|
||||
def isConfigured(self):
|
||||
return self.access_token and self.seaf_url
|
||||
|
||||
def getFilename(self):
|
||||
self.loadSettings()
|
||||
return ScreenCloud.formatFilename(self.nameFormat)
|
||||
|
||||
def upload(self, screenshot, name):
|
||||
self.loadSettings()
|
||||
#Make sure we have a up to date token
|
||||
if not self.seaf_lib:
|
||||
ScreenCloud.setError("Not configured properly")
|
||||
return False
|
||||
#Save to a temporary file
|
||||
timestamp = time.time()
|
||||
try:
|
||||
tmpFilename = QDesktopServices.storageLocation(QDesktopServices.TempLocation) + "/" + ScreenCloud.formatFilename(str(timestamp))
|
||||
except AttributeError:
|
||||
from PythonQt.QtCore import QStandardPaths #fix for Qt5
|
||||
tmpFilename = QStandardPaths.writableLocation(QStandardPaths.TempLocation) + "/" + ScreenCloud.formatFilename(str(timestamp))
|
||||
screenshot.save(QFile(tmpFilename), ScreenCloud.getScreenshotFormat())
|
||||
#Upload!
|
||||
link=None
|
||||
try:
|
||||
file = self.seaf_lib.upload(tmpFilename,self.getFilename(),self.lib_path)
|
||||
link = file.share()
|
||||
except Exception as e:
|
||||
ScreenCloud.setError("Failed to upload to seafile. " + e.message)
|
||||
return False
|
||||
if self.copyLink:
|
||||
ScreenCloud.setUrl(link)
|
||||
return True
|
||||
|
||||
def startAuthenticationProcess(self):
|
||||
self.saveSettings()
|
||||
self.loadSettings()
|
||||
|
||||
if self.access_token is not None:
|
||||
self.logOut()
|
||||
return
|
||||
|
||||
if self.settingsDialog.group_account.widget_loggedIn.serverUrlEdit.text \
|
||||
and self.settingsDialog.group_account.widget_loggedIn.usernameEdit.text \
|
||||
and self.settingsDialog.group_account.widget_loggedIn.passwordEdit.text:
|
||||
self.seaf_url = self.settingsDialog.group_account.widget_loggedIn.serverUrlEdit.text
|
||||
if True:
|
||||
#try:
|
||||
self.login(self.settingsDialog.group_account.widget_loggedIn.usernameEdit.text,self.settingsDialog.group_account.widget_loggedIn.passwordEdit.text)
|
||||
self.saveSettings()
|
||||
self.loadSettings()
|
||||
self.populateLibrarySelector()
|
||||
#except Exception as e:
|
||||
#QMessageBox.critical(self.settingsDialog, "Failed to login", "Verify your server url and credentials" + e.message)
|
||||
self.saveSettings()
|
||||
self.updateUi()
|
||||
|
||||
def locationUpdate(self):
|
||||
drop = self.settingsDialog.group_location.widget_location.libraryEditDrop
|
||||
selected_lib = drop.currentText
|
||||
if not drop.isEnabled(): return #not populated
|
||||
self.lib_path = self.settingsDialog.group_location.widget_location.pathEdit.text
|
||||
print self.lib_path, selected_lib
|
||||
if self.libs is None: return
|
||||
for lib in self.libs:
|
||||
if lib.name == selected_lib:
|
||||
self.seaf_lib = lib
|
||||
print "%s user selected" % selected_lib
|
||||
|
||||
|
||||
def login(self,user,password):
|
||||
cli = SeafileClient(self.seaf_url,user,password)
|
||||
self.access_token = cli.obtain_token()
|
||||
|
||||
def logOut(self):
|
||||
settings = QSettings()
|
||||
settings.beginGroup("uploaders")
|
||||
settings.beginGroup("seafile")
|
||||
settings.remove("auth-token")
|
||||
self.access_token = None
|
||||
settings.endGroup()
|
||||
settings.endGroup()
|
||||
self.loadSettings()
|
||||
self.updateUi()
|
||||
|
||||
def nameFormatEdited(self, nameFormat):
|
||||
self.settingsDialog.group_name.label_example.setText(ScreenCloud.formatFilename(nameFormat, False))
|
7
metadata.xml
Normal file
7
metadata.xml
Normal file
@ -0,0 +1,7 @@
|
||||
<metadata>
|
||||
<name>Seafile</name>
|
||||
<shortname>seafile</shortname>
|
||||
<className>SeafileUploader</className>
|
||||
<icon>icon.png</icon>
|
||||
<version>0.1</version>
|
||||
</metadata>
|
207
modules/Crypto/Cipher/AES.py
Normal file
207
modules/Crypto/Cipher/AES.py
Normal file
@ -0,0 +1,207 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Cipher/AES.py : AES
|
||||
#
|
||||
# ===================================================================
|
||||
# The contents of this file are dedicated to the public domain. To
|
||||
# the extent that dedication to the public domain is not available,
|
||||
# everyone is granted a worldwide, perpetual, royalty-free,
|
||||
# non-exclusive license to exercise all rights associated with the
|
||||
# contents of this file for any purpose whatsoever.
|
||||
# No rights are reserved.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
# SOFTWARE.
|
||||
# ===================================================================
|
||||
"""AES symmetric cipher
|
||||
|
||||
AES `(Advanced Encryption Standard)`__ is a symmetric block cipher standardized
|
||||
by NIST_ . It has a fixed data block size of 16 bytes.
|
||||
Its keys can be 128, 192, or 256 bits long.
|
||||
|
||||
AES is very fast and secure, and it is the de facto standard for symmetric
|
||||
encryption.
|
||||
|
||||
As an example, encryption can be done as follows:
|
||||
|
||||
>>> from Crypto.Cipher import AES
|
||||
>>> from Crypto.Random import get_random_bytes
|
||||
>>>
|
||||
>>> key = b'Sixteen byte key'
|
||||
>>> iv = get_random_bytes(16)
|
||||
>>> cipher = AES.new(key, AES.MODE_CFB, iv)
|
||||
>>> msg = iv + cipher.encrypt(b'Attack at dawn')
|
||||
|
||||
A more complicated example is based on CCM, (see `MODE_CCM`) an `AEAD`_ mode
|
||||
that provides both confidentiality and authentication for a message.
|
||||
It also allows message for the header to remain in the clear, whilst still
|
||||
being authenticated. The encryption is done as follows:
|
||||
|
||||
>>> from Crypto.Cipher import AES
|
||||
>>> from Crypto.Random import get_random_bytes
|
||||
>>>
|
||||
>>>
|
||||
>>> hdr = b'To your eyes only'
|
||||
>>> plaintext = b'Attack at dawn'
|
||||
>>> key = b'Sixteen byte key'
|
||||
>>> nonce = get_random_bytes(11)
|
||||
>>> cipher = AES.new(key, AES.MODE_CCM, nonce)
|
||||
>>> cipher.update(hdr)
|
||||
>>> msg = nonce, hdr, cipher.encrypt(plaintext), cipher.digest()
|
||||
|
||||
We assume that the tuple ``msg`` is transmitted to the receiver:
|
||||
|
||||
>>> nonce, hdr, ciphertext, mac = msg
|
||||
>>> key = b'Sixteen byte key'
|
||||
>>> cipher = AES.new(key, AES.MODE_CCM, nonce)
|
||||
>>> cipher.update(hdr)
|
||||
>>> plaintext = cipher.decrypt(ciphertext)
|
||||
>>> try:
|
||||
>>> cipher.verify(mac)
|
||||
>>> print "The message is authentic: hdr=%s, pt=%s" % (hdr, plaintext)
|
||||
>>> except ValueError:
|
||||
>>> print "Key incorrect or message corrupted"
|
||||
|
||||
.. __: http://en.wikipedia.org/wiki/Advanced_Encryption_Standard
|
||||
.. _NIST: http://csrc.nist.gov/publications/fips/fips197/fips-197.pdf
|
||||
.. _AEAD: http://blog.cryptographyengineering.com/2012/05/how-to-choose-authenticated-encryption.html
|
||||
|
||||
:undocumented: __revision__, __package__
|
||||
"""
|
||||
|
||||
__revision__ = "$Id$"
|
||||
|
||||
import sys
|
||||
if sys.version_info[0] == 2 and sys.version_info[1] == 1:
|
||||
from Crypto.Util.py21compat import *
|
||||
from Crypto.Cipher import blockalgo
|
||||
from Crypto.Cipher import _AES
|
||||
from Crypto.Util import cpuid
|
||||
# Import _AESNI. If AES-NI is not available or _AESNI has not been built, set
|
||||
# _AESNI to None.
|
||||
try:
|
||||
if cpuid.have_aes_ni():
|
||||
from Crypto.Cipher import _AESNI
|
||||
else:
|
||||
_AESNI = None
|
||||
except ImportError:
|
||||
_AESNI = None
|
||||
|
||||
class AESCipher (blockalgo.BlockAlgo):
|
||||
"""AES cipher object"""
|
||||
|
||||
def __init__(self, key, *args, **kwargs):
|
||||
"""Initialize an AES cipher object
|
||||
|
||||
See also `new()` at the module level."""
|
||||
|
||||
# Check if the use_aesni was specified.
|
||||
use_aesni = True
|
||||
if kwargs.has_key('use_aesni'):
|
||||
use_aesni = kwargs['use_aesni']
|
||||
del kwargs['use_aesni']
|
||||
|
||||
# Use _AESNI if the user requested AES-NI and it's available
|
||||
if _AESNI is not None and use_aesni:
|
||||
blockalgo.BlockAlgo.__init__(self, _AESNI, key, *args, **kwargs)
|
||||
else:
|
||||
blockalgo.BlockAlgo.__init__(self, _AES, key, *args, **kwargs)
|
||||
|
||||
def new(key, *args, **kwargs):
|
||||
"""Create a new AES cipher
|
||||
|
||||
:Parameters:
|
||||
key : byte string
|
||||
The secret key to use in the symmetric cipher.
|
||||
It must be 16 (*AES-128*), 24 (*AES-192*), or 32 (*AES-256*) bytes long.
|
||||
|
||||
Only in `MODE_SIV`, it needs to be 32, 48, or 64 bytes long.
|
||||
:Keywords:
|
||||
mode : a *MODE_** constant
|
||||
The chaining mode to use for encryption or decryption.
|
||||
Default is `MODE_ECB`.
|
||||
IV : byte string
|
||||
(*Only* `MODE_CBC`, `MODE_CFB`, `MODE_OFB`, `MODE_OPENPGP`).
|
||||
|
||||
The initialization vector to use for encryption or decryption.
|
||||
|
||||
It is ignored for `MODE_ECB` and `MODE_CTR`.
|
||||
|
||||
For `MODE_OPENPGP`, IV must be `block_size` bytes long for encryption
|
||||
and `block_size` +2 bytes for decryption (in the latter case, it is
|
||||
actually the *encrypted* IV which was prefixed to the ciphertext).
|
||||
It is mandatory.
|
||||
|
||||
For all other modes, it must be 16 bytes long.
|
||||
nonce : byte string
|
||||
(*Only* `MODE_CCM`, `MODE_EAX`, `MODE_GCM`, `MODE_SIV`).
|
||||
|
||||
A mandatory value that must never be reused for any other encryption.
|
||||
|
||||
For `MODE_CCM`, its length must be in the range ``[7..13]``.
|
||||
11 or 12 bytes are reasonable values in general. Bear in
|
||||
mind that with CCM there is a trade-off between nonce length and
|
||||
maximum message size.
|
||||
|
||||
For the other modes, there are no restrictions on its length,
|
||||
but it is recommended to use at least 16 bytes.
|
||||
counter : callable
|
||||
(*Only* `MODE_CTR`). A stateful function that returns the next
|
||||
*counter block*, which is a byte string of `block_size` bytes.
|
||||
For better performance, use `Crypto.Util.Counter`.
|
||||
segment_size : integer
|
||||
(*Only* `MODE_CFB`).The number of bits the plaintext and ciphertext
|
||||
are segmented in.
|
||||
It must be a multiple of 8. If 0 or not specified, it will be assumed to be 8.
|
||||
mac_len : integer
|
||||
(*Only* `MODE_CCM`). Length of the MAC, in bytes. It must be even and in
|
||||
the range ``[4..16]``. The default is 16.
|
||||
|
||||
(*Only* `MODE_EAX` and `MODE_GCM`). Length of the MAC, in bytes. It must be no
|
||||
larger than 16 bytes (which is the default).
|
||||
msg_len : integer
|
||||
(*Only* `MODE_CCM`). Length of the message to (de)cipher.
|
||||
If not specified, ``encrypt`` or ``decrypt`` may only be called once.
|
||||
assoc_len : integer
|
||||
(*Only* `MODE_CCM`). Length of the associated data.
|
||||
If not specified, all data is internally buffered.
|
||||
use_aesni : boolean
|
||||
Use AES-NI if available.
|
||||
|
||||
:Return: an `AESCipher` object
|
||||
"""
|
||||
return AESCipher(key, *args, **kwargs)
|
||||
|
||||
#: Electronic Code Book (ECB). See `blockalgo.MODE_ECB`.
|
||||
MODE_ECB = 1
|
||||
#: Cipher-Block Chaining (CBC). See `blockalgo.MODE_CBC`.
|
||||
MODE_CBC = 2
|
||||
#: Cipher FeedBack (CFB). See `blockalgo.MODE_CFB`.
|
||||
MODE_CFB = 3
|
||||
#: This mode should not be used.
|
||||
MODE_PGP = 4
|
||||
#: Output FeedBack (OFB). See `blockalgo.MODE_OFB`.
|
||||
MODE_OFB = 5
|
||||
#: CounTer Mode (CTR). See `blockalgo.MODE_CTR`.
|
||||
MODE_CTR = 6
|
||||
#: OpenPGP Mode. See `blockalgo.MODE_OPENPGP`.
|
||||
MODE_OPENPGP = 7
|
||||
#: Counter with CBC-MAC (CCM) Mode. See `blockalgo.MODE_CCM`.
|
||||
MODE_CCM = 8
|
||||
#: EAX Mode. See `blockalgo.MODE_EAX`.
|
||||
MODE_EAX = 9
|
||||
#: Syntethic Initialization Vector (SIV). See `blockalgo.MODE_SIV`.
|
||||
MODE_SIV = 10
|
||||
#: Galois Counter Mode (GCM). See `blockalgo.MODE_GCM`.
|
||||
MODE_GCM = 11
|
||||
#: Size of a data block (in bytes)
|
||||
block_size = 16
|
||||
#: Size of a key (in bytes)
|
||||
key_size = ( 16, 24, 32 )
|
||||
|
141
modules/Crypto/Cipher/ARC2.py
Normal file
141
modules/Crypto/Cipher/ARC2.py
Normal file
@ -0,0 +1,141 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Cipher/ARC2.py : ARC2.py
|
||||
#
|
||||
# ===================================================================
|
||||
# The contents of this file are dedicated to the public domain. To
|
||||
# the extent that dedication to the public domain is not available,
|
||||
# everyone is granted a worldwide, perpetual, royalty-free,
|
||||
# non-exclusive license to exercise all rights associated with the
|
||||
# contents of this file for any purpose whatsoever.
|
||||
# No rights are reserved.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
# SOFTWARE.
|
||||
# ===================================================================
|
||||
"""RC2 symmetric cipher
|
||||
|
||||
RC2_ (Rivest's Cipher version 2) is a symmetric block cipher designed
|
||||
by Ron Rivest in 1987. The cipher started as a proprietary design,
|
||||
that was reverse engineered and anonymously posted on Usenet in 1996.
|
||||
For this reason, the algorithm was first called *Alleged* RC2 (ARC2),
|
||||
since the company that owned RC2 (RSA Data Inc.) did not confirm whether
|
||||
the details leaked into public domain were really correct.
|
||||
|
||||
The company eventually published its full specification in RFC2268_.
|
||||
|
||||
RC2 has a fixed data block size of 8 bytes. Length of its keys can vary from
|
||||
8 to 128 bits. One particular property of RC2 is that the actual
|
||||
cryptographic strength of the key (*effective key length*) can be reduced
|
||||
via a parameter.
|
||||
|
||||
Even though RC2 is not cryptographically broken, it has not been analyzed as
|
||||
thoroughly as AES, which is also faster than RC2.
|
||||
|
||||
New designs should not use RC2.
|
||||
|
||||
As an example, encryption can be done as follows:
|
||||
|
||||
>>> from Crypto.Cipher import ARC2
|
||||
>>> from Crypto import Random
|
||||
>>>
|
||||
>>> key = b'Sixteen byte key'
|
||||
>>> iv = Random.new().read(ARC2.block_size)
|
||||
>>> cipher = ARC2.new(key, ARC2.MODE_CFB, iv)
|
||||
>>> msg = iv + cipher.encrypt(b'Attack at dawn')
|
||||
|
||||
.. _RC2: http://en.wikipedia.org/wiki/RC2
|
||||
.. _RFC2268: http://tools.ietf.org/html/rfc2268
|
||||
|
||||
:undocumented: __revision__, __package__
|
||||
"""
|
||||
|
||||
__revision__ = "$Id$"
|
||||
|
||||
from Crypto.Cipher import blockalgo
|
||||
from Crypto.Cipher import _ARC2
|
||||
|
||||
class RC2Cipher (blockalgo.BlockAlgo):
|
||||
"""RC2 cipher object"""
|
||||
|
||||
def __init__(self, key, *args, **kwargs):
|
||||
"""Initialize an ARC2 cipher object
|
||||
|
||||
See also `new()` at the module level."""
|
||||
blockalgo.BlockAlgo.__init__(self, _ARC2, key, *args, **kwargs)
|
||||
|
||||
def new(key, *args, **kwargs):
|
||||
"""Create a new RC2 cipher
|
||||
|
||||
:Parameters:
|
||||
key : byte string
|
||||
The secret key to use in the symmetric cipher.
|
||||
Its length can vary from 1 to 128 bytes.
|
||||
:Keywords:
|
||||
mode : a *MODE_** constant
|
||||
The chaining mode to use for encryption or decryption.
|
||||
Default is `MODE_ECB`.
|
||||
IV : byte string
|
||||
(*Only* `MODE_CBC`, `MODE_CFB`, `MODE_OFB`, `MODE_OPENPGP`).
|
||||
|
||||
The initialization vector to use for encryption or decryption.
|
||||
|
||||
It is ignored for `MODE_ECB` and `MODE_CTR`.
|
||||
|
||||
For `MODE_OPENPGP`, IV must be `block_size` bytes long for encryption
|
||||
and `block_size` +2 bytes for decryption (in the latter case, it is
|
||||
actually the *encrypted* IV which was prefixed to the ciphertext).
|
||||
It is mandatory.
|
||||
|
||||
For all other modes, it must be 8 bytes long.
|
||||
nonce : byte string
|
||||
(*Only* `MODE_EAX`).
|
||||
A mandatory value that must never be reused for any other encryption.
|
||||
There are no restrictions on its length, but it is recommended to
|
||||
use at least 16 bytes.
|
||||
counter : callable
|
||||
(*Only* `MODE_CTR`). A stateful function that returns the next
|
||||
*counter block*, which is a byte string of `block_size` bytes.
|
||||
For better performance, use `Crypto.Util.Counter`.
|
||||
mac_len : integer
|
||||
(*Only* `MODE_EAX`). Length of the MAC, in bytes.
|
||||
It must be no larger than 8 (which is the default).
|
||||
segment_size : integer
|
||||
(*Only* `MODE_CFB`).The number of bits the plaintext and ciphertext
|
||||
are segmented in.
|
||||
It must be a multiple of 8. If 0 or not specified, it will be assumed to be 8.
|
||||
effective_keylen : integer
|
||||
Maximum cryptographic strength of the key, in bits.
|
||||
It can vary from 0 to 1024. The default value is 1024.
|
||||
|
||||
:Return: an `RC2Cipher` object
|
||||
"""
|
||||
return RC2Cipher(key, *args, **kwargs)
|
||||
|
||||
#: Electronic Code Book (ECB). See `blockalgo.MODE_ECB`.
|
||||
MODE_ECB = 1
|
||||
#: Cipher-Block Chaining (CBC). See `blockalgo.MODE_CBC`.
|
||||
MODE_CBC = 2
|
||||
#: Cipher FeedBack (CFB). See `blockalgo.MODE_CFB`.
|
||||
MODE_CFB = 3
|
||||
#: This mode should not be used.
|
||||
MODE_PGP = 4
|
||||
#: Output FeedBack (OFB). See `blockalgo.MODE_OFB`.
|
||||
MODE_OFB = 5
|
||||
#: CounTer Mode (CTR). See `blockalgo.MODE_CTR`.
|
||||
MODE_CTR = 6
|
||||
#: OpenPGP Mode. See `blockalgo.MODE_OPENPGP`.
|
||||
MODE_OPENPGP = 7
|
||||
#: EAX Mode. See `blockalgo.MODE_EAX`.
|
||||
MODE_EAX = 9
|
||||
#: Size of a data block (in bytes)
|
||||
block_size = 8
|
||||
#: Size of a key (in bytes)
|
||||
key_size = xrange(1,16+1)
|
||||
|
141
modules/Crypto/Cipher/ARC4.py
Normal file
141
modules/Crypto/Cipher/ARC4.py
Normal file
@ -0,0 +1,141 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Cipher/ARC4.py : ARC4
|
||||
#
|
||||
# ===================================================================
|
||||
# The contents of this file are dedicated to the public domain. To
|
||||
# the extent that dedication to the public domain is not available,
|
||||
# everyone is granted a worldwide, perpetual, royalty-free,
|
||||
# non-exclusive license to exercise all rights associated with the
|
||||
# contents of this file for any purpose whatsoever.
|
||||
# No rights are reserved.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
# SOFTWARE.
|
||||
# ===================================================================
|
||||
"""ARC4 symmetric cipher
|
||||
|
||||
ARC4_ (Alleged RC4) is an implementation of RC4 (Rivest's Cipher version 4),
|
||||
a symmetric stream cipher designed by Ron Rivest in 1987.
|
||||
|
||||
The cipher started as a proprietary design, that was reverse engineered and
|
||||
anonymously posted on Usenet in 1994. The company that owns RC4 (RSA Data
|
||||
Inc.) never confirmed the correctness of the leaked algorithm.
|
||||
|
||||
Unlike RC2, the company has never published the full specification of RC4,
|
||||
of whom it still holds the trademark.
|
||||
|
||||
ARC4 keys can vary in length from 40 to 2048 bits.
|
||||
|
||||
One problem of ARC4 is that it does not take a nonce or an IV. If it is required
|
||||
to encrypt multiple messages with the same long-term key, a distinct
|
||||
independent nonce must be created for each message, and a short-term key must
|
||||
be derived from the combination of the long-term key and the nonce.
|
||||
Due to the weak key scheduling algorithm of RC2, the combination must be carried
|
||||
out with a complex function (e.g. a cryptographic hash) and not by simply
|
||||
concatenating key and nonce.
|
||||
|
||||
New designs should not use ARC4. A good alternative is AES
|
||||
(`Crypto.Cipher.AES`) in any of the modes that turn it into a stream cipher (OFB, CFB, or CTR).
|
||||
|
||||
As an example, encryption can be done as follows:
|
||||
|
||||
>>> from Crypto.Cipher import ARC4
|
||||
>>> from Crypto.Hash import SHA
|
||||
>>> from Crypto import Random
|
||||
>>>
|
||||
>>> key = b'Very long and confidential key'
|
||||
>>> nonce = Random.new().read(16)
|
||||
>>> tempkey = SHA.new(key+nonce).digest()
|
||||
>>> cipher = ARC4.new(tempkey)
|
||||
>>> msg = nonce + cipher.encrypt(b'Open the pod bay doors, HAL')
|
||||
|
||||
.. _ARC4: http://en.wikipedia.org/wiki/RC4
|
||||
|
||||
:undocumented: __revision__, __package__
|
||||
"""
|
||||
|
||||
__revision__ = "$Id$"
|
||||
|
||||
from Crypto.Util.py3compat import *
|
||||
from Crypto.Cipher import _ARC4
|
||||
|
||||
class ARC4Cipher:
|
||||
"""ARC4 cipher object"""
|
||||
|
||||
|
||||
def __init__(self, key, *args, **kwargs):
|
||||
"""Initialize an ARC4 cipher object
|
||||
|
||||
See also `new()` at the module level."""
|
||||
|
||||
if len(args)>0:
|
||||
ndrop = args[0]
|
||||
args = args[1:]
|
||||
else:
|
||||
ndrop = kwargs.get('drop', 0)
|
||||
if ndrop: del kwargs['drop']
|
||||
self._cipher = _ARC4.new(key, *args, **kwargs)
|
||||
if ndrop:
|
||||
# This is OK even if the cipher is used for decryption, since encrypt
|
||||
# and decrypt are actually the same thing with ARC4.
|
||||
self._cipher.encrypt(b('\x00')*ndrop)
|
||||
|
||||
self.block_size = self._cipher.block_size
|
||||
self.key_size = self._cipher.key_size
|
||||
|
||||
def encrypt(self, plaintext):
|
||||
"""Encrypt a piece of data.
|
||||
|
||||
:Parameters:
|
||||
plaintext : byte string
|
||||
The piece of data to encrypt. It can be of any size.
|
||||
:Return: the encrypted data (byte string, as long as the
|
||||
plaintext).
|
||||
"""
|
||||
return self._cipher.encrypt(plaintext)
|
||||
|
||||
def decrypt(self, ciphertext):
|
||||
"""Decrypt a piece of data.
|
||||
|
||||
:Parameters:
|
||||
ciphertext : byte string
|
||||
The piece of data to decrypt. It can be of any size.
|
||||
:Return: the decrypted data (byte string, as long as the
|
||||
ciphertext).
|
||||
"""
|
||||
return self._cipher.decrypt(ciphertext)
|
||||
|
||||
def new(key, *args, **kwargs):
|
||||
"""Create a new ARC4 cipher
|
||||
|
||||
:Parameters:
|
||||
key : byte string
|
||||
The secret key to use in the symmetric cipher.
|
||||
It can have any length, with a minimum of 40 bytes.
|
||||
Its cryptograpic strength is always capped to 2048 bits (256 bytes).
|
||||
:Keywords:
|
||||
drop : integer
|
||||
The amount of bytes to discard from the initial part of the keystream.
|
||||
In fact, such part has been found to be distinguishable from random
|
||||
data (while it shouldn't) and also correlated to key.
|
||||
|
||||
The recommended value is 3072_ bytes. The default value is 0.
|
||||
|
||||
:Return: an `ARC4Cipher` object
|
||||
|
||||
.. _3072: http://eprint.iacr.org/2002/067.pdf
|
||||
"""
|
||||
return ARC4Cipher(key, *args, **kwargs)
|
||||
|
||||
#: Size of a data block (in bytes)
|
||||
block_size = 1
|
||||
#: Size of a key (in bytes)
|
||||
key_size = xrange(1,256+1)
|
||||
|
132
modules/Crypto/Cipher/Blowfish.py
Normal file
132
modules/Crypto/Cipher/Blowfish.py
Normal file
@ -0,0 +1,132 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Cipher/Blowfish.py : Blowfish
|
||||
#
|
||||
# ===================================================================
|
||||
# The contents of this file are dedicated to the public domain. To
|
||||
# the extent that dedication to the public domain is not available,
|
||||
# everyone is granted a worldwide, perpetual, royalty-free,
|
||||
# non-exclusive license to exercise all rights associated with the
|
||||
# contents of this file for any purpose whatsoever.
|
||||
# No rights are reserved.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
# SOFTWARE.
|
||||
# ===================================================================
|
||||
"""Blowfish symmetric cipher
|
||||
|
||||
Blowfish_ is a symmetric block cipher designed by Bruce Schneier.
|
||||
|
||||
It has a fixed data block size of 8 bytes and its keys can vary in length
|
||||
from 32 to 448 bits (4 to 56 bytes).
|
||||
|
||||
Blowfish is deemed secure and it is fast. However, its keys should be chosen
|
||||
to be big enough to withstand a brute force attack (e.g. at least 16 bytes).
|
||||
|
||||
As an example, encryption can be done as follows:
|
||||
|
||||
>>> from Crypto.Cipher import Blowfish
|
||||
>>> from Crypto import Random
|
||||
>>> from struct import pack
|
||||
>>>
|
||||
>>> bs = Blowfish.block_size
|
||||
>>> key = b'An arbitrarily long key'
|
||||
>>> iv = Random.new().read(bs)
|
||||
>>> cipher = Blowfish.new(key, Blowfish.MODE_CBC, iv)
|
||||
>>> plaintext = b'docendo discimus '
|
||||
>>> plen = bs - divmod(len(plaintext),bs)[1]
|
||||
>>> padding = [plen]*plen
|
||||
>>> padding = pack('b'*plen, *padding)
|
||||
>>> msg = iv + cipher.encrypt(plaintext + padding)
|
||||
|
||||
.. _Blowfish: http://www.schneier.com/blowfish.html
|
||||
|
||||
:undocumented: __revision__, __package__
|
||||
"""
|
||||
|
||||
__revision__ = "$Id$"
|
||||
|
||||
from Crypto.Cipher import blockalgo
|
||||
from Crypto.Cipher import _Blowfish
|
||||
|
||||
class BlowfishCipher (blockalgo.BlockAlgo):
|
||||
"""Blowfish cipher object"""
|
||||
|
||||
def __init__(self, key, *args, **kwargs):
|
||||
"""Initialize a Blowfish cipher object
|
||||
|
||||
See also `new()` at the module level."""
|
||||
blockalgo.BlockAlgo.__init__(self, _Blowfish, key, *args, **kwargs)
|
||||
|
||||
def new(key, *args, **kwargs):
|
||||
"""Create a new Blowfish cipher
|
||||
|
||||
:Parameters:
|
||||
key : byte string
|
||||
The secret key to use in the symmetric cipher.
|
||||
Its length can vary from 4 to 56 bytes.
|
||||
:Keywords:
|
||||
mode : a *MODE_** constant
|
||||
The chaining mode to use for encryption or decryption.
|
||||
Default is `MODE_ECB`.
|
||||
IV : byte string
|
||||
(*Only* `MODE_CBC`, `MODE_CFB`, `MODE_OFB`, `MODE_OPENPGP`).
|
||||
|
||||
The initialization vector to use for encryption or decryption.
|
||||
|
||||
It is ignored for `MODE_ECB` and `MODE_CTR`.
|
||||
|
||||
For `MODE_OPENPGP`, IV must be `block_size` bytes long for encryption
|
||||
and `block_size` +2 bytes for decryption (in the latter case, it is
|
||||
actually the *encrypted* IV which was prefixed to the ciphertext).
|
||||
It is mandatory.
|
||||
|
||||
For all other modes, it must be 8 bytes long.
|
||||
nonce : byte string
|
||||
(*Only* `MODE_EAX`).
|
||||
A mandatory value that must never be reused for any other encryption.
|
||||
There are no restrictions on its length, but it is recommended to
|
||||
use at least 16 bytes.
|
||||
counter : callable
|
||||
(*Only* `MODE_CTR`). A stateful function that returns the next
|
||||
*counter block*, which is a byte string of `block_size` bytes.
|
||||
For better performance, use `Crypto.Util.Counter`.
|
||||
mac_len : integer
|
||||
(*Only* `MODE_EAX`). Length of the MAC, in bytes.
|
||||
It must be no larger than 8 (which is the default).
|
||||
segment_size : integer
|
||||
(*Only* `MODE_CFB`).The number of bits the plaintext and ciphertext
|
||||
are segmented in.
|
||||
It must be a multiple of 8. If 0 or not specified, it will be assumed to be 8.
|
||||
|
||||
:Return: a `BlowfishCipher` object
|
||||
"""
|
||||
return BlowfishCipher(key, *args, **kwargs)
|
||||
|
||||
#: Electronic Code Book (ECB). See `blockalgo.MODE_ECB`.
|
||||
MODE_ECB = 1
|
||||
#: Cipher-Block Chaining (CBC). See `blockalgo.MODE_CBC`.
|
||||
MODE_CBC = 2
|
||||
#: Cipher FeedBack (CFB). See `blockalgo.MODE_CFB`.
|
||||
MODE_CFB = 3
|
||||
#: This mode should not be used.
|
||||
MODE_PGP = 4
|
||||
#: Output FeedBack (OFB). See `blockalgo.MODE_OFB`.
|
||||
MODE_OFB = 5
|
||||
#: CounTer Mode (CTR). See `blockalgo.MODE_CTR`.
|
||||
MODE_CTR = 6
|
||||
#: OpenPGP Mode. See `blockalgo.MODE_OPENPGP`.
|
||||
MODE_OPENPGP = 7
|
||||
#: EAX Mode. See `blockalgo.MODE_EAX`.
|
||||
MODE_EAX = 9
|
||||
#: Size of a data block (in bytes)
|
||||
block_size = 8
|
||||
#: Size of a key (in bytes)
|
||||
key_size = xrange(4,56+1)
|
||||
|
134
modules/Crypto/Cipher/CAST.py
Normal file
134
modules/Crypto/Cipher/CAST.py
Normal file
@ -0,0 +1,134 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Cipher/CAST.py : CAST
|
||||
#
|
||||
# ===================================================================
|
||||
# The contents of this file are dedicated to the public domain. To
|
||||
# the extent that dedication to the public domain is not available,
|
||||
# everyone is granted a worldwide, perpetual, royalty-free,
|
||||
# non-exclusive license to exercise all rights associated with the
|
||||
# contents of this file for any purpose whatsoever.
|
||||
# No rights are reserved.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
# SOFTWARE.
|
||||
# ===================================================================
|
||||
"""CAST-128 symmetric cipher
|
||||
|
||||
CAST-128_ (or CAST5) is a symmetric block cipher specified in RFC2144_.
|
||||
|
||||
It has a fixed data block size of 8 bytes. Its key can vary in length
|
||||
from 40 to 128 bits.
|
||||
|
||||
CAST is deemed to be cryptographically secure, but its usage is not widespread.
|
||||
Keys of sufficient length should be used to prevent brute force attacks
|
||||
(128 bits are recommended).
|
||||
|
||||
As an example, encryption can be done as follows:
|
||||
|
||||
>>> from Crypto.Cipher import CAST
|
||||
>>> from Crypto import Random
|
||||
>>>
|
||||
>>> key = b'Sixteen byte key'
|
||||
>>> iv = Random.new().read(CAST.block_size)
|
||||
>>> cipher = CAST.new(key, CAST.MODE_OPENPGP, iv)
|
||||
>>> plaintext = b'sona si latine loqueris '
|
||||
>>> msg = cipher.encrypt(plaintext)
|
||||
>>>
|
||||
...
|
||||
>>> eiv = msg[:CAST.block_size+2]
|
||||
>>> ciphertext = msg[CAST.block_size+2:]
|
||||
>>> cipher = CAST.new(key, CAST.MODE_OPENPGP, eiv)
|
||||
>>> print cipher.decrypt(ciphertext)
|
||||
|
||||
.. _CAST-128: http://en.wikipedia.org/wiki/CAST-128
|
||||
.. _RFC2144: http://tools.ietf.org/html/rfc2144
|
||||
|
||||
:undocumented: __revision__, __package__
|
||||
"""
|
||||
|
||||
__revision__ = "$Id$"
|
||||
|
||||
from Crypto.Cipher import blockalgo
|
||||
from Crypto.Cipher import _CAST
|
||||
|
||||
class CAST128Cipher(blockalgo.BlockAlgo):
|
||||
"""CAST-128 cipher object"""
|
||||
|
||||
def __init__(self, key, *args, **kwargs):
|
||||
"""Initialize a CAST-128 cipher object
|
||||
|
||||
See also `new()` at the module level."""
|
||||
blockalgo.BlockAlgo.__init__(self, _CAST, key, *args, **kwargs)
|
||||
|
||||
def new(key, *args, **kwargs):
|
||||
"""Create a new CAST-128 cipher
|
||||
|
||||
:Parameters:
|
||||
key : byte string
|
||||
The secret key to use in the symmetric cipher.
|
||||
Its length may vary from 5 to 16 bytes.
|
||||
:Keywords:
|
||||
mode : a *MODE_** constant
|
||||
The chaining mode to use for encryption or decryption.
|
||||
Default is `MODE_ECB`.
|
||||
IV : byte string
|
||||
(*Only* `MODE_CBC`, `MODE_CFB`, `MODE_OFB`, `MODE_OPENPGP`).
|
||||
|
||||
The initialization vector to use for encryption or decryption.
|
||||
|
||||
It is ignored for `MODE_ECB` and `MODE_CTR`.
|
||||
|
||||
For `MODE_OPENPGP`, IV must be `block_size` bytes long for encryption
|
||||
and `block_size` +2 bytes for decryption (in the latter case, it is
|
||||
actually the *encrypted* IV which was prefixed to the ciphertext).
|
||||
It is mandatory.
|
||||
|
||||
For all other modes, it must be 8 bytes long.
|
||||
nonce : byte string
|
||||
(*Only* `MODE_EAX`).
|
||||
A mandatory value that must never be reused for any other encryption.
|
||||
There are no restrictions on its length, but it is recommended to
|
||||
use at least 16 bytes.
|
||||
counter : callable
|
||||
(*Only* `MODE_CTR`). A stateful function that returns the next
|
||||
*counter block*, which is a byte string of `block_size` bytes.
|
||||
For better performance, use `Crypto.Util.Counter`.
|
||||
mac_len : integer
|
||||
(*Only* `MODE_EAX`). Length of the MAC, in bytes.
|
||||
It must be no larger than 8 (which is the default).
|
||||
segment_size : integer
|
||||
(*Only* `MODE_CFB`).The number of bits the plaintext and ciphertext
|
||||
are segmented in.
|
||||
It must be a multiple of 8. If 0 or not specified, it will be assumed to be 8.
|
||||
|
||||
:Return: an `CAST128Cipher` object
|
||||
"""
|
||||
return CAST128Cipher(key, *args, **kwargs)
|
||||
|
||||
#: Electronic Code Book (ECB). See `blockalgo.MODE_ECB`.
|
||||
MODE_ECB = 1
|
||||
#: Cipher-Block Chaining (CBC). See `blockalgo.MODE_CBC`.
|
||||
MODE_CBC = 2
|
||||
#: Cipher FeedBack (CFB). See `blockalgo.MODE_CFB`.
|
||||
MODE_CFB = 3
|
||||
#: This mode should not be used.
|
||||
MODE_PGP = 4
|
||||
#: Output FeedBack (OFB). See `blockalgo.MODE_OFB`.
|
||||
MODE_OFB = 5
|
||||
#: CounTer Mode (CTR). See `blockalgo.MODE_CTR`.
|
||||
MODE_CTR = 6
|
||||
#: OpenPGP Mode. See `blockalgo.MODE_OPENPGP`.
|
||||
MODE_OPENPGP = 7
|
||||
#: EAX Mode. See `blockalgo.MODE_EAX`.
|
||||
MODE_EAX = 9
|
||||
#: Size of a data block (in bytes)
|
||||
block_size = 8
|
||||
#: Size of a key (in bytes)
|
||||
key_size = xrange(5,16+1)
|
129
modules/Crypto/Cipher/DES.py
Normal file
129
modules/Crypto/Cipher/DES.py
Normal file
@ -0,0 +1,129 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Cipher/DES.py : DES
|
||||
#
|
||||
# ===================================================================
|
||||
# The contents of this file are dedicated to the public domain. To
|
||||
# the extent that dedication to the public domain is not available,
|
||||
# everyone is granted a worldwide, perpetual, royalty-free,
|
||||
# non-exclusive license to exercise all rights associated with the
|
||||
# contents of this file for any purpose whatsoever.
|
||||
# No rights are reserved.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
# SOFTWARE.
|
||||
# ===================================================================
|
||||
"""DES symmetric cipher
|
||||
|
||||
DES `(Data Encryption Standard)`__ is a symmetric block cipher standardized
|
||||
by NIST_ . It has a fixed data block size of 8 bytes.
|
||||
Its keys are 64 bits long, even though 8 bits were used for integrity (now they
|
||||
are ignored) and do not contribute to securty.
|
||||
|
||||
DES is cryptographically secure, but its key length is too short by nowadays
|
||||
standards and it could be brute forced with some effort.
|
||||
|
||||
DES should not be used for new designs. Use `AES`.
|
||||
|
||||
As an example, encryption can be done as follows:
|
||||
|
||||
>>> from Crypto.Cipher import DES
|
||||
>>> from Crypto import Random
|
||||
>>>
|
||||
>>> key = b'-8B key-'
|
||||
>>> iv = Random.new().read(DES.block_size)
|
||||
>>> cipher = DES.new(key, DES.MODE_OFB, iv)
|
||||
>>> plaintext = b'sona si latine loqueris '
|
||||
>>> msg = iv + cipher.encrypt(plaintext)
|
||||
|
||||
.. __: http://en.wikipedia.org/wiki/Data_Encryption_Standard
|
||||
.. _NIST: http://csrc.nist.gov/publications/fips/fips46-3/fips46-3.pdf
|
||||
|
||||
:undocumented: __revision__, __package__
|
||||
"""
|
||||
|
||||
__revision__ = "$Id$"
|
||||
|
||||
from Crypto.Cipher import blockalgo
|
||||
from Crypto.Cipher import _DES
|
||||
|
||||
class DESCipher(blockalgo.BlockAlgo):
|
||||
"""DES cipher object"""
|
||||
|
||||
def __init__(self, key, *args, **kwargs):
|
||||
"""Initialize a DES cipher object
|
||||
|
||||
See also `new()` at the module level."""
|
||||
blockalgo.BlockAlgo.__init__(self, _DES, key, *args, **kwargs)
|
||||
|
||||
def new(key, *args, **kwargs):
|
||||
"""Create a new DES cipher
|
||||
|
||||
:Parameters:
|
||||
key : byte string
|
||||
The secret key to use in the symmetric cipher.
|
||||
It must be 8 byte long. The parity bits will be ignored.
|
||||
:Keywords:
|
||||
mode : a *MODE_** constant
|
||||
The chaining mode to use for encryption or decryption.
|
||||
Default is `MODE_ECB`.
|
||||
IV : byte string
|
||||
(*Only* `MODE_CBC`, `MODE_CFB`, `MODE_OFB`, `MODE_OPENPGP`).
|
||||
|
||||
The initialization vector to use for encryption or decryption.
|
||||
|
||||
It is ignored for `MODE_ECB` and `MODE_CTR`.
|
||||
|
||||
For `MODE_OPENPGP`, IV must be `block_size` bytes long for encryption
|
||||
and `block_size` +2 bytes for decryption (in the latter case, it is
|
||||
actually the *encrypted* IV which was prefixed to the ciphertext).
|
||||
It is mandatory.
|
||||
|
||||
For all other modes, it must be 8 bytes long.
|
||||
nonce : byte string
|
||||
(*Only* `MODE_EAX`).
|
||||
A mandatory value that must never be reused for any other encryption.
|
||||
There are no restrictions on its length, but it is recommended to
|
||||
use at least 16 bytes.
|
||||
counter : callable
|
||||
(*Only* `MODE_CTR`). A stateful function that returns the next
|
||||
*counter block*, which is a byte string of `block_size` bytes.
|
||||
For better performance, use `Crypto.Util.Counter`.
|
||||
mac_len : integer
|
||||
(*Only* `MODE_EAX`). Length of the MAC, in bytes.
|
||||
It must be no larger than 8 (which is the default).
|
||||
segment_size : integer
|
||||
(*Only* `MODE_CFB`).The number of bits the plaintext and ciphertext
|
||||
are segmented in.
|
||||
It must be a multiple of 8. If 0 or not specified, it will be assumed to be 8.
|
||||
|
||||
:Return: an `DESCipher` object
|
||||
"""
|
||||
return DESCipher(key, *args, **kwargs)
|
||||
|
||||
#: Electronic Code Book (ECB). See `blockalgo.MODE_ECB`.
|
||||
MODE_ECB = 1
|
||||
#: Cipher-Block Chaining (CBC). See `blockalgo.MODE_CBC`.
|
||||
MODE_CBC = 2
|
||||
#: Cipher FeedBack (CFB). See `blockalgo.MODE_CFB`.
|
||||
MODE_CFB = 3
|
||||
#: This mode should not be used.
|
||||
MODE_PGP = 4
|
||||
#: Output FeedBack (OFB). See `blockalgo.MODE_OFB`.
|
||||
MODE_OFB = 5
|
||||
#: CounTer Mode (CTR). See `blockalgo.MODE_CTR`.
|
||||
MODE_CTR = 6
|
||||
#: OpenPGP Mode. See `blockalgo.MODE_OPENPGP`.
|
||||
MODE_OPENPGP = 7
|
||||
#: EAX Mode. See `blockalgo.MODE_EAX`.
|
||||
MODE_EAX = 9
|
||||
#: Size of a data block (in bytes)
|
||||
block_size = 8
|
||||
#: Size of a key (in bytes)
|
||||
key_size = 8
|
144
modules/Crypto/Cipher/DES3.py
Normal file
144
modules/Crypto/Cipher/DES3.py
Normal file
@ -0,0 +1,144 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Cipher/DES3.py : DES3
|
||||
#
|
||||
# ===================================================================
|
||||
# The contents of this file are dedicated to the public domain. To
|
||||
# the extent that dedication to the public domain is not available,
|
||||
# everyone is granted a worldwide, perpetual, royalty-free,
|
||||
# non-exclusive license to exercise all rights associated with the
|
||||
# contents of this file for any purpose whatsoever.
|
||||
# No rights are reserved.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
# SOFTWARE.
|
||||
# ===================================================================
|
||||
"""Triple DES symmetric cipher
|
||||
|
||||
`Triple DES`__ (or TDES or TDEA or 3DES) is a symmetric block cipher standardized by NIST_.
|
||||
It has a fixed data block size of 8 bytes. Its keys are 128 (*Option 1*) or 192
|
||||
bits (*Option 2*) long.
|
||||
However, 1 out of 8 bits is used for redundancy and do not contribute to
|
||||
security. The effective key length is respectively 112 or 168 bits.
|
||||
|
||||
TDES consists of the concatenation of 3 simple `DES` ciphers.
|
||||
|
||||
The plaintext is first DES encrypted with *K1*, then decrypted with *K2*,
|
||||
and finally encrypted again with *K3*. The ciphertext is decrypted in the reverse manner.
|
||||
|
||||
The 192 bit key is a bundle of three 64 bit independent subkeys: *K1*, *K2*, and *K3*.
|
||||
|
||||
The 128 bit key is split into *K1* and *K2*, whereas *K1=K3*.
|
||||
|
||||
It is important that all subkeys are different, otherwise TDES would degrade to
|
||||
single `DES`.
|
||||
|
||||
TDES is cryptographically secure, even though it is neither as secure nor as fast
|
||||
as `AES`.
|
||||
|
||||
As an example, encryption can be done as follows:
|
||||
|
||||
>>> from Crypto.Cipher import DES3
|
||||
>>> from Crypto import Random
|
||||
>>> from Crypto.Util import Counter
|
||||
>>>
|
||||
>>> key = b'Sixteen byte key'
|
||||
>>> nonce = Random.new().read(DES3.block_size/2)
|
||||
>>> ctr = Counter.new(DES3.block_size*8/2, prefix=nonce)
|
||||
>>> cipher = DES3.new(key, DES3.MODE_CTR, counter=ctr)
|
||||
>>> plaintext = b'We are no longer the knights who say ni!'
|
||||
>>> msg = nonce + cipher.encrypt(plaintext)
|
||||
|
||||
.. __: http://en.wikipedia.org/wiki/Triple_DES
|
||||
.. _NIST: http://csrc.nist.gov/publications/nistpubs/800-67/SP800-67.pdf
|
||||
|
||||
:undocumented: __revision__, __package__
|
||||
"""
|
||||
|
||||
__revision__ = "$Id$"
|
||||
|
||||
from Crypto.Cipher import blockalgo
|
||||
from Crypto.Cipher import _DES3
|
||||
|
||||
class DES3Cipher(blockalgo.BlockAlgo):
|
||||
"""TDES cipher object"""
|
||||
|
||||
def __init__(self, key, *args, **kwargs):
|
||||
"""Initialize a TDES cipher object
|
||||
|
||||
See also `new()` at the module level."""
|
||||
blockalgo.BlockAlgo.__init__(self, _DES3, key, *args, **kwargs)
|
||||
|
||||
def new(key, *args, **kwargs):
|
||||
"""Create a new TDES cipher
|
||||
|
||||
:Parameters:
|
||||
key : byte string
|
||||
The secret key to use in the symmetric cipher.
|
||||
It must be 16 or 24 bytes long. The parity bits will be ignored.
|
||||
:Keywords:
|
||||
mode : a *MODE_** constant
|
||||
The chaining mode to use for encryption or decryption.
|
||||
Default is `MODE_ECB`.
|
||||
IV : byte string
|
||||
(*Only* `MODE_CBC`, `MODE_CFB`, `MODE_OFB`, `MODE_OPENPGP`).
|
||||
|
||||
The initialization vector to use for encryption or decryption.
|
||||
|
||||
It is ignored for `MODE_ECB` and `MODE_CTR`.
|
||||
|
||||
For `MODE_OPENPGP`, IV must be `block_size` bytes long for encryption
|
||||
and `block_size` +2 bytes for decryption (in the latter case, it is
|
||||
actually the *encrypted* IV which was prefixed to the ciphertext).
|
||||
It is mandatory.
|
||||
|
||||
For all other modes, it must be 8 bytes long.
|
||||
nonce : byte string
|
||||
(*Only* `MODE_EAX`).
|
||||
A mandatory value that must never be reused for any other encryption.
|
||||
There are no restrictions on its length, but it is recommended to
|
||||
use at least 16 bytes.
|
||||
counter : callable
|
||||
(*Only* `MODE_CTR`). A stateful function that returns the next
|
||||
*counter block*, which is a byte string of 8 bytes.
|
||||
For better performance, use `Crypto.Util.Counter`.
|
||||
mac_len : integer
|
||||
(*Only* `MODE_EAX`). Length of the MAC, in bytes.
|
||||
It must be no larger than 8 (which is the default).
|
||||
segment_size : integer
|
||||
(*Only* `MODE_CFB`).The number of bits the plaintext and ciphertext
|
||||
are segmented in.
|
||||
It must be a multiple of 8. If 0 or not specified, it will be assumed to be 8.
|
||||
|
||||
:Attention: it is important that all 8 byte subkeys are different,
|
||||
otherwise TDES would degrade to single `DES`.
|
||||
:Return: an `DES3Cipher` object
|
||||
"""
|
||||
return DES3Cipher(key, *args, **kwargs)
|
||||
|
||||
#: Electronic Code Book (ECB). See `blockalgo.MODE_ECB`.
|
||||
MODE_ECB = 1
|
||||
#: Cipher-Block Chaining (CBC). See `blockalgo.MODE_CBC`.
|
||||
MODE_CBC = 2
|
||||
#: Cipher FeedBack (CFB). See `blockalgo.MODE_CFB`.
|
||||
MODE_CFB = 3
|
||||
#: This mode should not be used.
|
||||
MODE_PGP = 4
|
||||
#: Output FeedBack (OFB). See `blockalgo.MODE_OFB`.
|
||||
MODE_OFB = 5
|
||||
#: CounTer Mode (CTR). See `blockalgo.MODE_CTR`.
|
||||
MODE_CTR = 6
|
||||
#: OpenPGP Mode. See `blockalgo.MODE_OPENPGP`.
|
||||
MODE_OPENPGP = 7
|
||||
#: EAX Mode. See `blockalgo.MODE_EAX`.
|
||||
MODE_EAX = 9
|
||||
#: Size of a data block (in bytes)
|
||||
block_size = 8
|
||||
#: Size of a key (in bytes)
|
||||
key_size = ( 16, 24 )
|
255
modules/Crypto/Cipher/PKCS1_OAEP.py
Normal file
255
modules/Crypto/Cipher/PKCS1_OAEP.py
Normal file
@ -0,0 +1,255 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Cipher/PKCS1_OAEP.py : PKCS#1 OAEP
|
||||
#
|
||||
# ===================================================================
|
||||
# The contents of this file are dedicated to the public domain. To
|
||||
# the extent that dedication to the public domain is not available,
|
||||
# everyone is granted a worldwide, perpetual, royalty-free,
|
||||
# non-exclusive license to exercise all rights associated with the
|
||||
# contents of this file for any purpose whatsoever.
|
||||
# No rights are reserved.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
# SOFTWARE.
|
||||
# ===================================================================
|
||||
|
||||
"""RSA encryption protocol according to PKCS#1 OAEP
|
||||
|
||||
See RFC3447__ or the `original RSA Labs specification`__ .
|
||||
|
||||
This scheme is more properly called ``RSAES-OAEP``.
|
||||
|
||||
As an example, a sender may encrypt a message in this way:
|
||||
|
||||
>>> from Crypto.Cipher import PKCS1_OAEP
|
||||
>>> from Crypto.PublicKey import RSA
|
||||
>>>
|
||||
>>> message = b'To be encrypted'
|
||||
>>> key = RSA.importKey(open('pubkey.der').read())
|
||||
>>> cipher = PKCS1_OAEP.new(key)
|
||||
>>> ciphertext = cipher.encrypt(message)
|
||||
|
||||
At the receiver side, decryption can be done using the private part of
|
||||
the RSA key:
|
||||
|
||||
>>> key = RSA.importKey(open('privkey.der').read())
|
||||
>>> cipher = PKCS1_OAP.new(key)
|
||||
>>> message = cipher.decrypt(ciphertext)
|
||||
|
||||
:undocumented: __revision__, __package__
|
||||
|
||||
.. __: http://www.ietf.org/rfc/rfc3447.txt
|
||||
.. __: http://www.rsa.com/rsalabs/node.asp?id=2125.
|
||||
"""
|
||||
|
||||
from __future__ import nested_scopes
|
||||
|
||||
__revision__ = "$Id$"
|
||||
__all__ = [ 'new', 'PKCS1OAEP_Cipher' ]
|
||||
|
||||
import Crypto.Signature.PKCS1_PSS
|
||||
import Crypto.Hash.SHA1
|
||||
|
||||
from Crypto.Util.py3compat import *
|
||||
import Crypto.Util.number
|
||||
from Crypto.Util.number import ceil_div
|
||||
from Crypto.Util.strxor import strxor
|
||||
|
||||
class PKCS1OAEP_Cipher:
|
||||
"""This cipher can perform PKCS#1 v1.5 OAEP encryption or decryption."""
|
||||
|
||||
def __init__(self, key, hashAlgo, mgfunc, label):
|
||||
"""Initialize this PKCS#1 OAEP cipher object.
|
||||
|
||||
:Parameters:
|
||||
key : an RSA key object
|
||||
If a private half is given, both encryption and decryption are possible.
|
||||
If a public half is given, only encryption is possible.
|
||||
hashAlgo : hash object
|
||||
The hash function to use. This can be a module under `Crypto.Hash`
|
||||
or an existing hash object created from any of such modules. If not specified,
|
||||
`Crypto.Hash.SHA1` is used.
|
||||
mgfunc : callable
|
||||
A mask generation function that accepts two parameters: a string to
|
||||
use as seed, and the lenth of the mask to generate, in bytes.
|
||||
If not specified, the standard MGF1 is used (a safe choice).
|
||||
label : byte string
|
||||
A label to apply to this particular encryption. If not specified,
|
||||
an empty string is used. Specifying a label does not improve
|
||||
security.
|
||||
|
||||
:attention: Modify the mask generation function only if you know what you are doing.
|
||||
Sender and receiver must use the same one.
|
||||
"""
|
||||
self._key = key
|
||||
|
||||
if hashAlgo:
|
||||
self._hashObj = hashAlgo
|
||||
else:
|
||||
self._hashObj = Crypto.Hash.SHA1
|
||||
|
||||
if mgfunc:
|
||||
self._mgf = mgfunc
|
||||
else:
|
||||
self._mgf = lambda x,y: Crypto.Signature.PKCS1_PSS.MGF1(x,y,self._hashObj)
|
||||
|
||||
self._label = label
|
||||
|
||||
def can_encrypt(self):
|
||||
"""Return True/1 if this cipher object can be used for encryption."""
|
||||
return self._key.can_encrypt()
|
||||
|
||||
def can_decrypt(self):
|
||||
"""Return True/1 if this cipher object can be used for decryption."""
|
||||
return self._key.can_decrypt()
|
||||
|
||||
def encrypt(self, message):
|
||||
"""Produce the PKCS#1 OAEP encryption of a message.
|
||||
|
||||
This function is named ``RSAES-OAEP-ENCRYPT``, and is specified in
|
||||
section 7.1.1 of RFC3447.
|
||||
|
||||
:Parameters:
|
||||
message : byte string
|
||||
The message to encrypt, also known as plaintext. It can be of
|
||||
variable length, but not longer than the RSA modulus (in bytes)
|
||||
minus 2, minus twice the hash output size.
|
||||
|
||||
:Return: A byte string, the ciphertext in which the message is encrypted.
|
||||
It is as long as the RSA modulus (in bytes).
|
||||
:Raise ValueError:
|
||||
If the RSA key length is not sufficiently long to deal with the given
|
||||
message.
|
||||
"""
|
||||
# TODO: Verify the key is RSA
|
||||
|
||||
randFunc = self._key._randfunc
|
||||
|
||||
# See 7.1.1 in RFC3447
|
||||
modBits = Crypto.Util.number.size(self._key.n)
|
||||
k = ceil_div(modBits,8) # Convert from bits to bytes
|
||||
hLen = self._hashObj.digest_size
|
||||
mLen = len(message)
|
||||
|
||||
# Step 1b
|
||||
ps_len = k-mLen-2*hLen-2
|
||||
if ps_len<0:
|
||||
raise ValueError("Plaintext is too long.")
|
||||
# Step 2a
|
||||
lHash = self._hashObj.new(self._label).digest()
|
||||
# Step 2b
|
||||
ps = bchr(0x00)*ps_len
|
||||
# Step 2c
|
||||
db = lHash + ps + bchr(0x01) + message
|
||||
# Step 2d
|
||||
ros = randFunc(hLen)
|
||||
# Step 2e
|
||||
dbMask = self._mgf(ros, k-hLen-1)
|
||||
# Step 2f
|
||||
maskedDB = strxor(db, dbMask)
|
||||
# Step 2g
|
||||
seedMask = self._mgf(maskedDB, hLen)
|
||||
# Step 2h
|
||||
maskedSeed = strxor(ros, seedMask)
|
||||
# Step 2i
|
||||
em = bchr(0x00) + maskedSeed + maskedDB
|
||||
# Step 3a (OS2IP), step 3b (RSAEP), part of step 3c (I2OSP)
|
||||
m = self._key.encrypt(em, 0)[0]
|
||||
# Complete step 3c (I2OSP)
|
||||
c = bchr(0x00)*(k-len(m)) + m
|
||||
return c
|
||||
|
||||
def decrypt(self, ct):
|
||||
"""Decrypt a PKCS#1 OAEP ciphertext.
|
||||
|
||||
This function is named ``RSAES-OAEP-DECRYPT``, and is specified in
|
||||
section 7.1.2 of RFC3447.
|
||||
|
||||
:Parameters:
|
||||
ct : byte string
|
||||
The ciphertext that contains the message to recover.
|
||||
|
||||
:Return: A byte string, the original message.
|
||||
:Raise ValueError:
|
||||
If the ciphertext length is incorrect, or if the decryption does not
|
||||
succeed.
|
||||
:Raise TypeError:
|
||||
If the RSA key has no private half.
|
||||
"""
|
||||
# TODO: Verify the key is RSA
|
||||
|
||||
# See 7.1.2 in RFC3447
|
||||
modBits = Crypto.Util.number.size(self._key.n)
|
||||
k = ceil_div(modBits,8) # Convert from bits to bytes
|
||||
hLen = self._hashObj.digest_size
|
||||
|
||||
# Step 1b and 1c
|
||||
if len(ct) != k or k<hLen+2:
|
||||
raise ValueError("Ciphertext with incorrect length.")
|
||||
# Step 2a (O2SIP), 2b (RSADP), and part of 2c (I2OSP)
|
||||
m = self._key.decrypt(ct)
|
||||
# Complete step 2c (I2OSP)
|
||||
em = bchr(0x00)*(k-len(m)) + m
|
||||
# Step 3a
|
||||
lHash = self._hashObj.new(self._label).digest()
|
||||
# Step 3b
|
||||
y = em[0]
|
||||
# y must be 0, but we MUST NOT check it here in order not to
|
||||
# allow attacks like Manger's (http://dl.acm.org/citation.cfm?id=704143)
|
||||
maskedSeed = em[1:hLen+1]
|
||||
maskedDB = em[hLen+1:]
|
||||
# Step 3c
|
||||
seedMask = self._mgf(maskedDB, hLen)
|
||||
# Step 3d
|
||||
seed = strxor(maskedSeed, seedMask)
|
||||
# Step 3e
|
||||
dbMask = self._mgf(seed, k-hLen-1)
|
||||
# Step 3f
|
||||
db = strxor(maskedDB, dbMask)
|
||||
# Step 3g
|
||||
valid = 1
|
||||
one = db[hLen:].find(bchr(0x01))
|
||||
lHash1 = db[:hLen]
|
||||
if lHash1!=lHash:
|
||||
valid = 0
|
||||
if one<0:
|
||||
valid = 0
|
||||
if bord(y)!=0:
|
||||
valid = 0
|
||||
if not valid:
|
||||
raise ValueError("Incorrect decryption.")
|
||||
# Step 4
|
||||
return db[hLen+one+1:]
|
||||
|
||||
def new(key, hashAlgo=None, mgfunc=None, label=b('')):
|
||||
"""Return a cipher object `PKCS1OAEP_Cipher` that can be used to perform PKCS#1 OAEP encryption or decryption.
|
||||
|
||||
:Parameters:
|
||||
key : RSA key object
|
||||
The key to use to encrypt or decrypt the message. This is a `Crypto.PublicKey.RSA` object.
|
||||
Decryption is only possible if *key* is a private RSA key.
|
||||
hashAlgo : hash object
|
||||
The hash function to use. This can be a module under `Crypto.Hash`
|
||||
or an existing hash object created from any of such modules. If not specified,
|
||||
`Crypto.Hash.SHA1` is used.
|
||||
mgfunc : callable
|
||||
A mask generation function that accepts two parameters: a string to
|
||||
use as seed, and the lenth of the mask to generate, in bytes.
|
||||
If not specified, the standard MGF1 is used (a safe choice).
|
||||
label : byte string
|
||||
A label to apply to this particular encryption. If not specified,
|
||||
an empty string is used. Specifying a label does not improve
|
||||
security.
|
||||
|
||||
:attention: Modify the mask generation function only if you know what you are doing.
|
||||
Sender and receiver must use the same one.
|
||||
"""
|
||||
return PKCS1OAEP_Cipher(key, hashAlgo, mgfunc, label)
|
||||
|
226
modules/Crypto/Cipher/PKCS1_v1_5.py
Normal file
226
modules/Crypto/Cipher/PKCS1_v1_5.py
Normal file
@ -0,0 +1,226 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Cipher/PKCS1-v1_5.py : PKCS#1 v1.5
|
||||
#
|
||||
# ===================================================================
|
||||
# The contents of this file are dedicated to the public domain. To
|
||||
# the extent that dedication to the public domain is not available,
|
||||
# everyone is granted a worldwide, perpetual, royalty-free,
|
||||
# non-exclusive license to exercise all rights associated with the
|
||||
# contents of this file for any purpose whatsoever.
|
||||
# No rights are reserved.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
# SOFTWARE.
|
||||
# ===================================================================
|
||||
|
||||
"""RSA encryption protocol according to PKCS#1 v1.5
|
||||
|
||||
See RFC3447__ or the `original RSA Labs specification`__ .
|
||||
|
||||
This scheme is more properly called ``RSAES-PKCS1-v1_5``.
|
||||
|
||||
**If you are designing a new protocol, consider using the more robust PKCS#1 OAEP.**
|
||||
|
||||
As an example, a sender may encrypt a message in this way:
|
||||
|
||||
>>> from Crypto.Cipher import PKCS1_v1_5
|
||||
>>> from Crypto.PublicKey import RSA
|
||||
>>> from Crypto.Hash import SHA
|
||||
>>>
|
||||
>>> message = b'To be encrypted'
|
||||
>>> h = SHA.new(message)
|
||||
>>>
|
||||
>>> key = RSA.importKey(open('pubkey.der').read())
|
||||
>>> cipher = PKCS1_v1_5.new(key)
|
||||
>>> ciphertext = cipher.encrypt(message+h.digest())
|
||||
|
||||
At the receiver side, decryption can be done using the private part of
|
||||
the RSA key:
|
||||
|
||||
>>> From Crypto.Hash import SHA
|
||||
>>> from Crypto import Random
|
||||
>>>
|
||||
>>> key = RSA.importKey(open('privkey.der').read())
|
||||
>>>
|
||||
>>> dsize = SHA.digest_size
|
||||
>>> sentinel = Random.new().read(15+dsize) # Let's assume that average data length is 15
|
||||
>>>
|
||||
>>> cipher = PKCS1_v1_5.new(key)
|
||||
>>> message = cipher.decrypt(ciphertext, sentinel)
|
||||
>>>
|
||||
>>> digest = SHA.new(message[:-dsize]).digest()
|
||||
>>> if digest==message[-dsize:]: # Note how we DO NOT look for the sentinel
|
||||
>>> print "Encryption was correct."
|
||||
>>> else:
|
||||
>>> print "Encryption was not correct."
|
||||
|
||||
:undocumented: __revision__, __package__
|
||||
|
||||
.. __: http://www.ietf.org/rfc/rfc3447.txt
|
||||
.. __: http://www.rsa.com/rsalabs/node.asp?id=2125.
|
||||
"""
|
||||
|
||||
__revision__ = "$Id$"
|
||||
__all__ = [ 'new', 'PKCS115_Cipher' ]
|
||||
|
||||
from Crypto.Util.number import ceil_div
|
||||
from Crypto.Util.py3compat import *
|
||||
import Crypto.Util.number
|
||||
|
||||
class PKCS115_Cipher:
|
||||
"""This cipher can perform PKCS#1 v1.5 RSA encryption or decryption."""
|
||||
|
||||
def __init__(self, key):
|
||||
"""Initialize this PKCS#1 v1.5 cipher object.
|
||||
|
||||
:Parameters:
|
||||
key : an RSA key object
|
||||
If a private half is given, both encryption and decryption are possible.
|
||||
If a public half is given, only encryption is possible.
|
||||
"""
|
||||
self._key = key
|
||||
|
||||
def can_encrypt(self):
|
||||
"""Return True if this cipher object can be used for encryption."""
|
||||
return self._key.can_encrypt()
|
||||
|
||||
def can_decrypt(self):
|
||||
"""Return True if this cipher object can be used for decryption."""
|
||||
return self._key.can_decrypt()
|
||||
|
||||
def encrypt(self, message):
|
||||
"""Produce the PKCS#1 v1.5 encryption of a message.
|
||||
|
||||
This function is named ``RSAES-PKCS1-V1_5-ENCRYPT``, and is specified in
|
||||
section 7.2.1 of RFC3447.
|
||||
For a complete example see `Crypto.Cipher.PKCS1_v1_5`.
|
||||
|
||||
:Parameters:
|
||||
message : byte string
|
||||
The message to encrypt, also known as plaintext. It can be of
|
||||
variable length, but not longer than the RSA modulus (in bytes) minus 11.
|
||||
|
||||
:Return: A byte string, the ciphertext in which the message is encrypted.
|
||||
It is as long as the RSA modulus (in bytes).
|
||||
:Raise ValueError:
|
||||
If the RSA key length is not sufficiently long to deal with the given
|
||||
message.
|
||||
|
||||
"""
|
||||
# TODO: Verify the key is RSA
|
||||
|
||||
randFunc = self._key._randfunc
|
||||
|
||||
# See 7.2.1 in RFC3447
|
||||
modBits = Crypto.Util.number.size(self._key.n)
|
||||
k = ceil_div(modBits,8) # Convert from bits to bytes
|
||||
mLen = len(message)
|
||||
|
||||
# Step 1
|
||||
if mLen > k-11:
|
||||
raise ValueError("Plaintext is too long.")
|
||||
# Step 2a
|
||||
class nonZeroRandByte:
|
||||
def __init__(self, rf): self.rf=rf
|
||||
def __call__(self, c):
|
||||
while bord(c)==0x00: c=self.rf(1)[0]
|
||||
return c
|
||||
ps = tobytes(map(nonZeroRandByte(randFunc), randFunc(k-mLen-3)))
|
||||
# Step 2b
|
||||
em = b('\x00\x02') + ps + bchr(0x00) + message
|
||||
# Step 3a (OS2IP), step 3b (RSAEP), part of step 3c (I2OSP)
|
||||
m = self._key.encrypt(em, 0)[0]
|
||||
# Complete step 3c (I2OSP)
|
||||
c = bchr(0x00)*(k-len(m)) + m
|
||||
return c
|
||||
|
||||
def decrypt(self, ct, sentinel):
|
||||
"""Decrypt a PKCS#1 v1.5 ciphertext.
|
||||
|
||||
This function is named ``RSAES-PKCS1-V1_5-DECRYPT``, and is specified in
|
||||
section 7.2.2 of RFC3447.
|
||||
For a complete example see `Crypto.Cipher.PKCS1_v1_5`.
|
||||
|
||||
:Parameters:
|
||||
ct : byte string
|
||||
The ciphertext that contains the message to recover.
|
||||
sentinel : any type
|
||||
The object to return to indicate that an error was detected during decryption.
|
||||
|
||||
:Return: A byte string. It is either the original message or the ``sentinel`` (in case of an error).
|
||||
:Raise ValueError:
|
||||
If the ciphertext length is incorrect
|
||||
:Raise TypeError:
|
||||
If the RSA key has no private half.
|
||||
|
||||
:attention:
|
||||
You should **never** let the party who submitted the ciphertext know that
|
||||
this function returned the ``sentinel`` value.
|
||||
Armed with such knowledge (for a fair amount of carefully crafted but invalid ciphertexts),
|
||||
an attacker is able to recontruct the plaintext of any other encryption that were carried out
|
||||
with the same RSA public key (see `Bleichenbacher's`__ attack).
|
||||
|
||||
In general, it should not be possible for the other party to distinguish
|
||||
whether processing at the server side failed because the value returned
|
||||
was a ``sentinel`` as opposed to a random, invalid message.
|
||||
|
||||
In fact, the second option is not that unlikely: encryption done according to PKCS#1 v1.5
|
||||
embeds no good integrity check. There is roughly one chance
|
||||
in 2^16 for a random ciphertext to be returned as a valid message
|
||||
(although random looking).
|
||||
|
||||
It is therefore advisabled to:
|
||||
|
||||
1. Select as ``sentinel`` a value that resembles a plausable random, invalid message.
|
||||
2. Not report back an error as soon as you detect a ``sentinel`` value.
|
||||
Put differently, you should not explicitly check if the returned value is the ``sentinel`` or not.
|
||||
3. Cover all possible errors with a single, generic error indicator.
|
||||
4. Embed into the definition of ``message`` (at the protocol level) a digest (e.g. ``SHA-1``).
|
||||
It is recommended for it to be the rightmost part ``message``.
|
||||
5. Where possible, monitor the number of errors due to ciphertexts originating from the same party,
|
||||
and slow down the rate of the requests from such party (or even blacklist it altogether).
|
||||
|
||||
**If you are designing a new protocol, consider using the more robust PKCS#1 OAEP.**
|
||||
|
||||
.. __: http://www.bell-labs.com/user/bleichen/papers/pkcs.ps
|
||||
|
||||
"""
|
||||
|
||||
# TODO: Verify the key is RSA
|
||||
|
||||
# See 7.2.1 in RFC3447
|
||||
modBits = Crypto.Util.number.size(self._key.n)
|
||||
k = ceil_div(modBits,8) # Convert from bits to bytes
|
||||
|
||||
# Step 1
|
||||
if len(ct) != k:
|
||||
raise ValueError("Ciphertext with incorrect length.")
|
||||
# Step 2a (O2SIP), 2b (RSADP), and part of 2c (I2OSP)
|
||||
m = self._key.decrypt(ct)
|
||||
# Complete step 2c (I2OSP)
|
||||
em = bchr(0x00)*(k-len(m)) + m
|
||||
# Step 3
|
||||
sep = em.find(bchr(0x00),2)
|
||||
if not em.startswith(b('\x00\x02')) or sep<10:
|
||||
return sentinel
|
||||
# Step 4
|
||||
return em[sep+1:]
|
||||
|
||||
def new(key):
|
||||
"""Return a cipher object `PKCS115_Cipher` that can be used to perform PKCS#1 v1.5 encryption or decryption.
|
||||
|
||||
:Parameters:
|
||||
key : RSA key object
|
||||
The key to use to encrypt or decrypt the message. This is a `Crypto.PublicKey.RSA` object.
|
||||
Decryption is only possible if *key* is a private RSA key.
|
||||
|
||||
"""
|
||||
return PKCS115_Cipher(key)
|
||||
|
86
modules/Crypto/Cipher/XOR.py
Normal file
86
modules/Crypto/Cipher/XOR.py
Normal file
@ -0,0 +1,86 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Cipher/XOR.py : XOR
|
||||
#
|
||||
# ===================================================================
|
||||
# The contents of this file are dedicated to the public domain. To
|
||||
# the extent that dedication to the public domain is not available,
|
||||
# everyone is granted a worldwide, perpetual, royalty-free,
|
||||
# non-exclusive license to exercise all rights associated with the
|
||||
# contents of this file for any purpose whatsoever.
|
||||
# No rights are reserved.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
# SOFTWARE.
|
||||
# ===================================================================
|
||||
"""XOR toy cipher
|
||||
|
||||
XOR is one the simplest stream ciphers. Encryption and decryption are
|
||||
performed by XOR-ing data with a keystream made by contatenating
|
||||
the key.
|
||||
|
||||
Do not use it for real applications!
|
||||
|
||||
:undocumented: __revision__, __package__
|
||||
"""
|
||||
|
||||
__revision__ = "$Id$"
|
||||
|
||||
from Crypto.Cipher import _XOR
|
||||
|
||||
class XORCipher:
|
||||
"""XOR cipher object"""
|
||||
|
||||
def __init__(self, key, *args, **kwargs):
|
||||
"""Initialize a XOR cipher object
|
||||
|
||||
See also `new()` at the module level."""
|
||||
self._cipher = _XOR.new(key, *args, **kwargs)
|
||||
self.block_size = self._cipher.block_size
|
||||
self.key_size = self._cipher.key_size
|
||||
|
||||
def encrypt(self, plaintext):
|
||||
"""Encrypt a piece of data.
|
||||
|
||||
:Parameters:
|
||||
plaintext : byte string
|
||||
The piece of data to encrypt. It can be of any size.
|
||||
:Return: the encrypted data (byte string, as long as the
|
||||
plaintext).
|
||||
"""
|
||||
return self._cipher.encrypt(plaintext)
|
||||
|
||||
def decrypt(self, ciphertext):
|
||||
"""Decrypt a piece of data.
|
||||
|
||||
:Parameters:
|
||||
ciphertext : byte string
|
||||
The piece of data to decrypt. It can be of any size.
|
||||
:Return: the decrypted data (byte string, as long as the
|
||||
ciphertext).
|
||||
"""
|
||||
return self._cipher.decrypt(ciphertext)
|
||||
|
||||
def new(key, *args, **kwargs):
|
||||
"""Create a new XOR cipher
|
||||
|
||||
:Parameters:
|
||||
key : byte string
|
||||
The secret key to use in the symmetric cipher.
|
||||
Its length may vary from 1 to 32 bytes.
|
||||
|
||||
:Return: an `XORCipher` object
|
||||
"""
|
||||
return XORCipher(key, *args, **kwargs)
|
||||
|
||||
#: Size of a data block (in bytes)
|
||||
block_size = 1
|
||||
#: Size of a key (in bytes)
|
||||
key_size = xrange(1,32+1)
|
||||
|
83
modules/Crypto/Cipher/__init__.py
Normal file
83
modules/Crypto/Cipher/__init__.py
Normal file
@ -0,0 +1,83 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# ===================================================================
|
||||
# The contents of this file are dedicated to the public domain. To
|
||||
# the extent that dedication to the public domain is not available,
|
||||
# everyone is granted a worldwide, perpetual, royalty-free,
|
||||
# non-exclusive license to exercise all rights associated with the
|
||||
# contents of this file for any purpose whatsoever.
|
||||
# No rights are reserved.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
# SOFTWARE.
|
||||
# ===================================================================
|
||||
|
||||
"""Symmetric- and asymmetric-key encryption algorithms.
|
||||
|
||||
Encryption algorithms transform plaintext in some way that
|
||||
is dependent on a key or key pair, producing ciphertext.
|
||||
|
||||
Symmetric algorithms
|
||||
--------------------
|
||||
|
||||
Encryption can easily be reversed, if (and, hopefully, only if)
|
||||
one knows the same key.
|
||||
In other words, sender and receiver share the same key.
|
||||
|
||||
The symmetric encryption modules here all support the interface described in PEP
|
||||
272, "API for Block Encryption Algorithms".
|
||||
|
||||
If you don't know which algorithm to choose, use AES because it's
|
||||
standard and has undergone a fair bit of examination.
|
||||
|
||||
======================== ======= ========================
|
||||
Module name Type Description
|
||||
======================== ======= ========================
|
||||
`Crypto.Cipher.AES` Block Advanced Encryption Standard
|
||||
`Crypto.Cipher.ARC2` Block Alleged RC2
|
||||
`Crypto.Cipher.ARC4` Stream Alleged RC4
|
||||
`Crypto.Cipher.Blowfish` Block Blowfish
|
||||
`Crypto.Cipher.CAST` Block CAST
|
||||
`Crypto.Cipher.DES` Block The Data Encryption Standard.
|
||||
Very commonly used in the past,
|
||||
but today its 56-bit keys are too small.
|
||||
`Crypto.Cipher.DES3` Block Triple DES.
|
||||
`Crypto.Cipher.XOR` Stream The simple XOR cipher.
|
||||
======================== ======= ========================
|
||||
|
||||
|
||||
Asymmetric algorithms
|
||||
---------------------
|
||||
|
||||
For asymmetric algorithms, the key to be used for decryption is totally
|
||||
different and cannot be derived in a feasible way from the key used
|
||||
for encryption. Put differently, sender and receiver each own one half
|
||||
of a key pair. The encryption key is often called ``public`` whereas
|
||||
the decryption key is called ``private``.
|
||||
|
||||
========================== =======================
|
||||
Module name Description
|
||||
========================== =======================
|
||||
`Crypto.Cipher.PKCS1_v1_5` PKCS#1 v1.5 encryption, based on RSA key pairs
|
||||
`Crypto.Cipher.PKCS1_OAEP` PKCS#1 OAEP encryption, based on RSA key pairs
|
||||
========================== =======================
|
||||
|
||||
:undocumented: __revision__, __package__, _AES, _ARC2, _ARC4, _Blowfish
|
||||
_CAST, _DES, _DES3, _XOR
|
||||
"""
|
||||
|
||||
__all__ = ['AES', 'ARC2', 'ARC4',
|
||||
'Blowfish', 'CAST', 'DES', 'DES3',
|
||||
'XOR',
|
||||
'PKCS1_v1_5', 'PKCS1_OAEP'
|
||||
]
|
||||
|
||||
__revision__ = "$Id$"
|
||||
|
||||
|
1036
modules/Crypto/Cipher/blockalgo.py
Normal file
1036
modules/Crypto/Cipher/blockalgo.py
Normal file
File diff suppressed because it is too large
Load Diff
343
modules/Crypto/Hash/CMAC.py
Normal file
343
modules/Crypto/Hash/CMAC.py
Normal file
@ -0,0 +1,343 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Hash/CMAC.py - Implements the CMAC algorithm
|
||||
#
|
||||
# ===================================================================
|
||||
# The contents of this file are dedicated to the public domain. To
|
||||
# the extent that dedication to the public domain is not available,
|
||||
# everyone is granted a worldwide, perpetual, royalty-free,
|
||||
# non-exclusive license to exercise all rights associated with the
|
||||
# contents of this file for any purpose whatsoever.
|
||||
# No rights are reserved.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
# SOFTWARE.
|
||||
# ===================================================================
|
||||
|
||||
"""CMAC (Cipher-based Message Authentication Code) algorithm
|
||||
|
||||
CMAC is a MAC defined in `NIST SP 800-38B`_ and in RFC4493_ (for AES only)
|
||||
and constructed using a block cipher. It was originally known as `OMAC1`_.
|
||||
|
||||
The algorithm is sometimes named *X-CMAC* where *X* is the name
|
||||
of the cipher (e.g. AES-CMAC).
|
||||
|
||||
This is an example showing how to *create* an AES-CMAC:
|
||||
|
||||
>>> from Crypto.Hash import CMAC
|
||||
>>> from Crypto.Cipher import AES
|
||||
>>>
|
||||
>>> secret = b'Sixteen byte key'
|
||||
>>> cobj = CMAC.new(secret, ciphermod=AES)
|
||||
>>> cobj.update(b'Hello')
|
||||
>>> print cobj.hexdigest()
|
||||
|
||||
And this is an example showing how to *check* an AES-CMAC:
|
||||
|
||||
>>> from Crypto.Hash import CMAC
|
||||
>>> from Crypto.Cipher import AES
|
||||
>>>
|
||||
>>> # We have received a message 'msg' together
|
||||
>>> # with its MAC 'mac'
|
||||
>>>
|
||||
>>> secret = b'Sixteen byte key'
|
||||
>>> cobj = CMAC.new(secret, ciphermod=AES)
|
||||
>>> cobj.update(msg)
|
||||
>>> try:
|
||||
>>> cobj.verify(mac)
|
||||
>>> print "The message '%s' is authentic" % msg
|
||||
>>> except ValueError:
|
||||
>>> print "The message or the key is wrong"
|
||||
|
||||
.. _`NIST SP 800-38B`: http://csrc.nist.gov/publications/nistpubs/800-38B/SP_800-38B.pdf
|
||||
.. _RFC4493: http://www.ietf.org/rfc/rfc4493.txt
|
||||
.. _OMAC1: http://www.nuee.nagoya-u.ac.jp/labs/tiwata/omac/omac.html
|
||||
"""
|
||||
|
||||
__all__ = ['new', 'digest_size', 'CMAC' ]
|
||||
|
||||
import sys
|
||||
if sys.version_info[0] == 2 and sys.version_info[1] == 1:
|
||||
from Crypto.Util.py21compat import *
|
||||
from Crypto.Util.py3compat import *
|
||||
|
||||
from binascii import unhexlify
|
||||
|
||||
from Crypto.Util.strxor import strxor
|
||||
from Crypto.Util.number import long_to_bytes, bytes_to_long
|
||||
|
||||
#: The size of the authentication tag produced by the MAC.
|
||||
digest_size = None
|
||||
|
||||
def _shift_bytes(bs, xor_lsb=0):
|
||||
num = (bytes_to_long(bs)<<1) ^ xor_lsb
|
||||
return long_to_bytes(num, len(bs))[-len(bs):]
|
||||
|
||||
class _SmoothMAC(object):
|
||||
"""Turn a MAC that only operates on aligned blocks of data
|
||||
into a MAC with granularity of 1 byte."""
|
||||
|
||||
def __init__(self, block_size, msg=b(""), min_digest=0):
|
||||
self._bs = block_size
|
||||
#: Data waiting to be MAC-ed
|
||||
self._buffer = []
|
||||
self._buffer_len = 0
|
||||
#: Data received via update()
|
||||
self._total_len = 0
|
||||
#: Minimum amount of bytes required by the final digest step
|
||||
self._min_digest = min_digest
|
||||
#: Block MAC object
|
||||
self._mac = None
|
||||
#: Cached digest
|
||||
self._tag = None
|
||||
if msg:
|
||||
self.update(msg)
|
||||
|
||||
def can_reduce(self):
|
||||
return (self._mac is not None)
|
||||
|
||||
def get_len(self):
|
||||
return self._total_len
|
||||
|
||||
def zero_pad(self):
|
||||
if self._buffer_len & (self._bs-1):
|
||||
npad = self._bs - self._buffer_len & (self._bs-1)
|
||||
self._buffer.append(bchr(0)*npad)
|
||||
self._buffer_len += npad
|
||||
|
||||
def update(self, data):
|
||||
# Optimization (try not to copy data if possible)
|
||||
if self._buffer_len==0 and self.can_reduce() and\
|
||||
self._min_digest==0 and len(data)%self._bs==0:
|
||||
self._update(data)
|
||||
self._total_len += len(data)
|
||||
return
|
||||
|
||||
self._buffer.append(data)
|
||||
self._buffer_len += len(data)
|
||||
self._total_len += len(data)
|
||||
|
||||
# Feed data into MAC
|
||||
blocks, rem = divmod(self._buffer_len, self._bs)
|
||||
if rem<self._min_digest:
|
||||
blocks -= 1
|
||||
if blocks>0 and self.can_reduce():
|
||||
aligned_data = blocks*self._bs
|
||||
buf = b("").join(self._buffer)
|
||||
self._update(buf[:aligned_data])
|
||||
self._buffer = [ buf[aligned_data:] ]
|
||||
self._buffer_len -= aligned_data
|
||||
|
||||
def _deep_copy(self, target):
|
||||
# Copy everything by self._mac, since we don't know how to
|
||||
target._buffer = self._buffer[:]
|
||||
for m in [ '_bs', '_buffer_len', '_total_len', '_min_digest', '_tag' ]:
|
||||
setattr(target, m, getattr(self, m))
|
||||
|
||||
def _update(self, data_block):
|
||||
"""Delegate to the implementation the update
|
||||
of the MAC state given some new *block aligned* data."""
|
||||
raise NotImplementedError("_update() must be still implemented")
|
||||
|
||||
def _digest(self, left_data):
|
||||
"""Delegate to the implementation the computation
|
||||
of the final MAC given the current MAC state
|
||||
and the last piece of data (not block aligned)."""
|
||||
raise NotImplementedError("_digest() must be still implemented")
|
||||
|
||||
def digest(self):
|
||||
if self._tag:
|
||||
return self._tag
|
||||
if self._buffer_len>0:
|
||||
self.update(b(""))
|
||||
left_data = b("").join(self._buffer)
|
||||
self._tag = self._digest(left_data)
|
||||
return self._tag
|
||||
|
||||
class CMAC(_SmoothMAC):
|
||||
"""Class that implements CMAC"""
|
||||
|
||||
#: The size of the authentication tag produced by the MAC.
|
||||
digest_size = None
|
||||
|
||||
def __init__(self, key, msg = None, ciphermod = None):
|
||||
"""Create a new CMAC object.
|
||||
|
||||
:Parameters:
|
||||
key : byte string
|
||||
secret key for the CMAC object.
|
||||
The key must be valid for the underlying cipher algorithm.
|
||||
For instance, it must be 16 bytes long for AES-128.
|
||||
msg : byte string
|
||||
The very first chunk of the message to authenticate.
|
||||
It is equivalent to an early call to `update`. Optional.
|
||||
ciphermod : module
|
||||
A cipher module from `Crypto.Cipher`.
|
||||
The cipher's block size must be 64 or 128 bits.
|
||||
It is recommended to use `Crypto.Cipher.AES`.
|
||||
"""
|
||||
|
||||
if ciphermod is None:
|
||||
raise TypeError("ciphermod must be specified (try AES)")
|
||||
|
||||
_SmoothMAC.__init__(self, ciphermod.block_size, msg, 1)
|
||||
|
||||
self._key = key
|
||||
self._factory = ciphermod
|
||||
|
||||
# Section 5.3 of NIST SP 800 38B
|
||||
if ciphermod.block_size==8:
|
||||
const_Rb = 0x1B
|
||||
elif ciphermod.block_size==16:
|
||||
const_Rb = 0x87
|
||||
else:
|
||||
raise TypeError("CMAC requires a cipher with a block size of 8 or 16 bytes, not %d" %
|
||||
(ciphermod.block_size,))
|
||||
self.digest_size = ciphermod.block_size
|
||||
|
||||
# Compute sub-keys
|
||||
cipher = ciphermod.new(key, ciphermod.MODE_ECB)
|
||||
l = cipher.encrypt(bchr(0)*ciphermod.block_size)
|
||||
if bord(l[0]) & 0x80:
|
||||
self._k1 = _shift_bytes(l, const_Rb)
|
||||
else:
|
||||
self._k1 = _shift_bytes(l)
|
||||
if bord(self._k1[0]) & 0x80:
|
||||
self._k2 = _shift_bytes(self._k1, const_Rb)
|
||||
else:
|
||||
self._k2 = _shift_bytes(self._k1)
|
||||
|
||||
# Initialize CBC cipher with zero IV
|
||||
self._IV = bchr(0)*ciphermod.block_size
|
||||
self._mac = ciphermod.new(key, ciphermod.MODE_CBC, self._IV)
|
||||
|
||||
def update(self, msg):
|
||||
"""Continue authentication of a message by consuming the next chunk of data.
|
||||
|
||||
Repeated calls are equivalent to a single call with the concatenation
|
||||
of all the arguments. In other words:
|
||||
|
||||
>>> m.update(a); m.update(b)
|
||||
|
||||
is equivalent to:
|
||||
|
||||
>>> m.update(a+b)
|
||||
|
||||
:Parameters:
|
||||
msg : byte string
|
||||
The next chunk of the message being authenticated
|
||||
"""
|
||||
|
||||
_SmoothMAC.update(self, msg)
|
||||
|
||||
def _update(self, data_block):
|
||||
self._IV = self._mac.encrypt(data_block)[-self._mac.block_size:]
|
||||
|
||||
def copy(self):
|
||||
"""Return a copy ("clone") of the MAC object.
|
||||
|
||||
The copy will have the same internal state as the original MAC
|
||||
object.
|
||||
This can be used to efficiently compute the MAC of strings that
|
||||
share a common initial substring.
|
||||
|
||||
:Returns: A `CMAC` object
|
||||
"""
|
||||
obj = CMAC(self._key, ciphermod=self._factory)
|
||||
|
||||
_SmoothMAC._deep_copy(self, obj)
|
||||
obj._mac = self._factory.new(self._key, self._factory.MODE_CBC, self._IV)
|
||||
for m in [ '_tag', '_k1', '_k2', '_IV']:
|
||||
setattr(obj, m, getattr(self, m))
|
||||
return obj
|
||||
|
||||
def digest(self):
|
||||
"""Return the **binary** (non-printable) MAC of the message that has
|
||||
been authenticated so far.
|
||||
|
||||
This method does not change the state of the MAC object.
|
||||
You can continue updating the object after calling this function.
|
||||
|
||||
:Return: A byte string of `digest_size` bytes. It may contain non-ASCII
|
||||
characters, including null bytes.
|
||||
"""
|
||||
return _SmoothMAC.digest(self)
|
||||
|
||||
def _digest(self, last_data):
|
||||
if len(last_data)==self._bs:
|
||||
last_block = strxor(last_data, self._k1)
|
||||
else:
|
||||
last_block = strxor(last_data+bchr(128)+
|
||||
bchr(0)*(self._bs-1-len(last_data)), self._k2)
|
||||
tag = self._mac.encrypt(last_block)
|
||||
return tag
|
||||
|
||||
def hexdigest(self):
|
||||
"""Return the **printable** MAC of the message that has been
|
||||
authenticated so far.
|
||||
|
||||
This method does not change the state of the MAC object.
|
||||
|
||||
:Return: A string of 2* `digest_size` bytes. It contains only
|
||||
hexadecimal ASCII digits.
|
||||
"""
|
||||
return "".join(["%02x" % bord(x)
|
||||
for x in tuple(self.digest())])
|
||||
|
||||
def verify(self, mac_tag):
|
||||
"""Verify that a given **binary** MAC (computed by another party) is valid.
|
||||
|
||||
:Parameters:
|
||||
mac_tag : byte string
|
||||
The expected MAC of the message.
|
||||
:Raises ValueError:
|
||||
if the MAC does not match. It means that the message
|
||||
has been tampered with or that the MAC key is incorrect.
|
||||
"""
|
||||
|
||||
mac = self.digest()
|
||||
res = 0
|
||||
# Constant-time comparison
|
||||
for x,y in zip(mac, mac_tag):
|
||||
res |= bord(x) ^ bord(y)
|
||||
if res or len(mac_tag)!=self.digest_size:
|
||||
raise ValueError("MAC check failed")
|
||||
|
||||
def hexverify(self, hex_mac_tag):
|
||||
"""Verify that a given **printable** MAC (computed by another party) is valid.
|
||||
|
||||
:Parameters:
|
||||
hex_mac_tag : string
|
||||
The expected MAC of the message, as a hexadecimal string.
|
||||
:Raises ValueError:
|
||||
if the MAC does not match. It means that the message
|
||||
has been tampered with or that the MAC key is incorrect.
|
||||
"""
|
||||
|
||||
self.verify(unhexlify(tobytes(hex_mac_tag)))
|
||||
|
||||
def new(key, msg = None, ciphermod = None):
|
||||
"""Create a new CMAC object.
|
||||
|
||||
:Parameters:
|
||||
key : byte string
|
||||
secret key for the CMAC object.
|
||||
The key must be valid for the underlying cipher algorithm.
|
||||
For instance, it must be 16 bytes long for AES-128.
|
||||
msg : byte string
|
||||
The very first chunk of the message to authenticate.
|
||||
It is equivalent to an early call to `CMAC.update`. Optional.
|
||||
ciphermod : module
|
||||
A cipher module from `Crypto.Cipher`.
|
||||
The cipher's block size must be 64 or 128 bits.
|
||||
Default is `Crypto.Cipher.AES`.
|
||||
|
||||
:Returns: A `CMAC` object
|
||||
"""
|
||||
return CMAC(key, msg, ciphermod)
|
263
modules/Crypto/Hash/HMAC.py
Normal file
263
modules/Crypto/Hash/HMAC.py
Normal file
@ -0,0 +1,263 @@
|
||||
# HMAC.py - Implements the HMAC algorithm as described by RFC 2104.
|
||||
#
|
||||
# ===================================================================
|
||||
# Portions Copyright (c) 2001, 2002, 2003 Python Software Foundation;
|
||||
# All Rights Reserved
|
||||
#
|
||||
# This file contains code from the Python 2.2 hmac.py module (the
|
||||
# "Original Code"), with modifications made after it was incorporated
|
||||
# into PyCrypto (the "Modifications").
|
||||
#
|
||||
# To the best of our knowledge, the Python Software Foundation is the
|
||||
# copyright holder of the Original Code, and has licensed it under the
|
||||
# Python 2.2 license. See the file LEGAL/copy/LICENSE.python-2.2 for
|
||||
# details.
|
||||
#
|
||||
# The Modifications to this file are dedicated to the public domain.
|
||||
# To the extent that dedication to the public domain is not available,
|
||||
# everyone is granted a worldwide, perpetual, royalty-free,
|
||||
# non-exclusive license to exercise all rights associated with the
|
||||
# contents of this file for any purpose whatsoever. No rights are
|
||||
# reserved.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
# SOFTWARE.
|
||||
# ===================================================================
|
||||
|
||||
|
||||
"""HMAC (Hash-based Message Authentication Code) algorithm
|
||||
|
||||
HMAC is a MAC defined in RFC2104_ and FIPS-198_ and constructed using
|
||||
a cryptograpic hash algorithm.
|
||||
It is usually named *HMAC-X*, where *X* is the hash algorithm; for
|
||||
instance *HMAC-SHA1* or *HMAC-MD5*.
|
||||
|
||||
The strength of an HMAC depends on:
|
||||
|
||||
- the strength of the hash algorithm
|
||||
- the length and entropy of the secret key
|
||||
|
||||
This is an example showing how to *create* a MAC:
|
||||
|
||||
>>> from Crypto.Hash import HMAC
|
||||
>>>
|
||||
>>> secret = b'Swordfish'
|
||||
>>> h = HMAC.new(secret)
|
||||
>>> h.update(b'Hello')
|
||||
>>> print h.hexdigest()
|
||||
|
||||
This is an example showing how to *check* a MAC:
|
||||
|
||||
>>> from Crypto.Hash import HMAC
|
||||
>>>
|
||||
>>> # We have received a message 'msg' together
|
||||
>>> # with its MAC 'mac'
|
||||
>>>
|
||||
>>> secret = b'Swordfish'
|
||||
>>> h = HMAC.new(secret)
|
||||
>>> h.update(msg)
|
||||
>>> try:
|
||||
>>> h.verify(mac)
|
||||
>>> print "The message '%s' is authentic" % msg
|
||||
>>> except ValueError:
|
||||
>>> print "The message or the key is wrong"
|
||||
|
||||
.. _RFC2104: http://www.ietf.org/rfc/rfc2104.txt
|
||||
.. _FIPS-198: http://csrc.nist.gov/publications/fips/fips198/fips-198a.pdf
|
||||
"""
|
||||
|
||||
# This is just a copy of the Python 2.2 HMAC module, modified to work when
|
||||
# used on versions of Python before 2.2.
|
||||
|
||||
__revision__ = "$Id$"
|
||||
|
||||
__all__ = ['new', 'digest_size', 'HMAC' ]
|
||||
|
||||
from binascii import unhexlify
|
||||
|
||||
from Crypto.Util.strxor import strxor_c
|
||||
from Crypto.Util.py3compat import *
|
||||
|
||||
#: The size of the authentication tag produced by the MAC.
|
||||
#: It matches the digest size on the underlying
|
||||
#: hashing module used.
|
||||
digest_size = None
|
||||
|
||||
class HMAC:
|
||||
"""Class that implements HMAC"""
|
||||
|
||||
#: The size of the authentication tag produced by the MAC.
|
||||
#: It matches the digest size on the underlying
|
||||
#: hashing module used.
|
||||
digest_size = None
|
||||
|
||||
def __init__(self, key, msg = None, digestmod = None):
|
||||
"""Create a new HMAC object.
|
||||
|
||||
:Parameters:
|
||||
key : byte string
|
||||
secret key for the MAC object.
|
||||
It must be long enough to match the expected security level of the
|
||||
MAC. However, there is no benefit in using keys longer than the
|
||||
`digest_size` of the underlying hash algorithm.
|
||||
msg : byte string
|
||||
The very first chunk of the message to authenticate.
|
||||
It is equivalent to an early call to `update()`. Optional.
|
||||
:Parameter digestmod:
|
||||
The hash algorithm the HMAC is based on.
|
||||
Default is `Crypto.Hash.MD5`.
|
||||
:Type digestmod:
|
||||
A hash module or object instantiated from `Crypto.Hash`
|
||||
"""
|
||||
if digestmod is None:
|
||||
import MD5
|
||||
digestmod = MD5
|
||||
|
||||
self.digestmod = digestmod
|
||||
self.outer = digestmod.new()
|
||||
self.inner = digestmod.new()
|
||||
try:
|
||||
self.digest_size = digestmod.digest_size
|
||||
except AttributeError:
|
||||
self.digest_size = len(self.outer.digest())
|
||||
|
||||
try:
|
||||
# The block size is 128 bytes for SHA384 and SHA512 and 64 bytes
|
||||
# for the others hash function
|
||||
blocksize = digestmod.block_size
|
||||
except AttributeError:
|
||||
blocksize = 64
|
||||
|
||||
ipad = 0x36
|
||||
opad = 0x5C
|
||||
|
||||
if len(key) > blocksize:
|
||||
key = digestmod.new(key).digest()
|
||||
|
||||
key = key + bchr(0) * (blocksize - len(key))
|
||||
self.outer.update(strxor_c(key, opad))
|
||||
self.inner.update(strxor_c(key, ipad))
|
||||
if (msg):
|
||||
self.update(msg)
|
||||
|
||||
def update(self, msg):
|
||||
"""Continue authentication of a message by consuming the next chunk of data.
|
||||
|
||||
Repeated calls are equivalent to a single call with the concatenation
|
||||
of all the arguments. In other words:
|
||||
|
||||
>>> m.update(a); m.update(b)
|
||||
|
||||
is equivalent to:
|
||||
|
||||
>>> m.update(a+b)
|
||||
|
||||
:Parameters:
|
||||
msg : byte string
|
||||
The next chunk of the message being authenticated
|
||||
"""
|
||||
|
||||
self.inner.update(msg)
|
||||
|
||||
def copy(self):
|
||||
"""Return a copy ("clone") of the MAC object.
|
||||
|
||||
The copy will have the same internal state as the original MAC
|
||||
object.
|
||||
This can be used to efficiently compute the MAC of strings that
|
||||
share a common initial substring.
|
||||
|
||||
:Returns: An `HMAC` object
|
||||
"""
|
||||
other = HMAC(b(""))
|
||||
other.digestmod = self.digestmod
|
||||
other.inner = self.inner.copy()
|
||||
other.outer = self.outer.copy()
|
||||
return other
|
||||
|
||||
def digest(self):
|
||||
"""Return the **binary** (non-printable) MAC of the message that has
|
||||
been authenticated so far.
|
||||
|
||||
This method does not change the state of the MAC object.
|
||||
You can continue updating the object after calling this function.
|
||||
|
||||
:Return: A byte string of `digest_size` bytes. It may contain non-ASCII
|
||||
characters, including null bytes.
|
||||
"""
|
||||
|
||||
h = self.outer.copy()
|
||||
h.update(self.inner.digest())
|
||||
return h.digest()
|
||||
|
||||
def verify(self, mac_tag):
|
||||
"""Verify that a given **binary** MAC (computed by another party) is valid.
|
||||
|
||||
:Parameters:
|
||||
mac_tag : byte string
|
||||
The expected MAC of the message.
|
||||
:Raises ValueError:
|
||||
if the MAC does not match. It means that the message
|
||||
has been tampered with or that the MAC key is incorrect.
|
||||
"""
|
||||
|
||||
mac = self.digest()
|
||||
res = 0
|
||||
# Constant-time comparison
|
||||
for x,y in zip(mac, mac_tag):
|
||||
res |= bord(x) ^ bord(y)
|
||||
if res or len(mac_tag)!=self.digest_size:
|
||||
raise ValueError("MAC check failed")
|
||||
|
||||
def hexdigest(self):
|
||||
"""Return the **printable** MAC of the message that has been
|
||||
authenticated so far.
|
||||
|
||||
This method does not change the state of the MAC object.
|
||||
|
||||
:Return: A string of 2* `digest_size` bytes. It contains only
|
||||
hexadecimal ASCII digits.
|
||||
"""
|
||||
return "".join(["%02x" % bord(x)
|
||||
for x in tuple(self.digest())])
|
||||
|
||||
def hexverify(self, hex_mac_tag):
|
||||
"""Verify that a given **printable** MAC (computed by another party) is valid.
|
||||
|
||||
:Parameters:
|
||||
hex_mac_tag : string
|
||||
The expected MAC of the message, as a hexadecimal string.
|
||||
:Raises ValueError:
|
||||
if the MAC does not match. It means that the message
|
||||
has been tampered with or that the MAC key is incorrect.
|
||||
"""
|
||||
|
||||
self.verify(unhexlify(tobytes(hex_mac_tag)))
|
||||
|
||||
def new(key, msg = None, digestmod = None):
|
||||
"""Create a new HMAC object.
|
||||
|
||||
:Parameters:
|
||||
key : byte string
|
||||
key for the MAC object.
|
||||
It must be long enough to match the expected security level of the
|
||||
MAC. However, there is no benefit in using keys longer than the
|
||||
`digest_size` of the underlying hash algorithm.
|
||||
msg : byte string
|
||||
The very first chunk of the message to authenticate.
|
||||
It is equivalent to an early call to `HMAC.update()`.
|
||||
Optional.
|
||||
:Parameter digestmod:
|
||||
The hash to use to implement the HMAC. Default is `Crypto.Hash.MD5`.
|
||||
:Type digestmod:
|
||||
A hash module or instantiated object from `Crypto.Hash`
|
||||
:Returns: An `HMAC` object
|
||||
"""
|
||||
return HMAC(key, msg, digestmod)
|
||||
|
92
modules/Crypto/Hash/MD5.py
Normal file
92
modules/Crypto/Hash/MD5.py
Normal file
@ -0,0 +1,92 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# ===================================================================
|
||||
# The contents of this file are dedicated to the public domain. To
|
||||
# the extent that dedication to the public domain is not available,
|
||||
# everyone is granted a worldwide, perpetual, royalty-free,
|
||||
# non-exclusive license to exercise all rights associated with the
|
||||
# contents of this file for any purpose whatsoever.
|
||||
# No rights are reserved.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
# SOFTWARE.
|
||||
# ===================================================================
|
||||
|
||||
"""MD5 cryptographic hash algorithm.
|
||||
|
||||
MD5 is specified in RFC1321_ and produces the 128 bit digest of a message.
|
||||
|
||||
>>> from Crypto.Hash import MD5
|
||||
>>>
|
||||
>>> h = MD5.new()
|
||||
>>> h.update(b'Hello')
|
||||
>>> print h.hexdigest()
|
||||
|
||||
MD5 stand for Message Digest version 5, and it was invented by Rivest in 1991.
|
||||
|
||||
This algorithm is insecure. Do not use it for new designs.
|
||||
|
||||
.. _RFC1321: http://tools.ietf.org/html/rfc1321
|
||||
"""
|
||||
|
||||
from __future__ import nested_scopes
|
||||
|
||||
_revision__ = "$Id$"
|
||||
|
||||
__all__ = ['new', 'block_size', 'digest_size']
|
||||
|
||||
from Crypto.Util.py3compat import *
|
||||
if sys.version_info[0] == 2 and sys.version_info[1] == 1:
|
||||
from Crypto.Util.py21compat import *
|
||||
|
||||
def __make_constructor():
|
||||
try:
|
||||
# The md5 module is deprecated in Python 2.6, so use hashlib when possible.
|
||||
from hashlib import md5 as _hash_new
|
||||
except ImportError:
|
||||
from md5 import new as _hash_new
|
||||
|
||||
h = _hash_new()
|
||||
if hasattr(h, 'new') and hasattr(h, 'name') and hasattr(h, 'digest_size') and hasattr(h, 'block_size'):
|
||||
# The module from stdlib has the API that we need. Just use it.
|
||||
return _hash_new
|
||||
else:
|
||||
# Wrap the hash object in something that gives us the expected API.
|
||||
_copy_sentinel = object()
|
||||
class _MD5(object):
|
||||
digest_size = 16
|
||||
block_size = 64
|
||||
name = "md5"
|
||||
def __init__(self, *args):
|
||||
if args and args[0] is _copy_sentinel:
|
||||
self._h = args[1]
|
||||
else:
|
||||
self._h = _hash_new(*args)
|
||||
def copy(self):
|
||||
return _MD5(_copy_sentinel, self._h.copy())
|
||||
def update(self, *args):
|
||||
f = self.update = self._h.update
|
||||
f(*args)
|
||||
def digest(self):
|
||||
f = self.digest = self._h.digest
|
||||
return f()
|
||||
def hexdigest(self):
|
||||
f = self.hexdigest = self._h.hexdigest
|
||||
return f()
|
||||
_MD5.new = _MD5
|
||||
return _MD5
|
||||
|
||||
new = __make_constructor()
|
||||
del __make_constructor
|
||||
|
||||
#: The size of the resulting hash in bytes.
|
||||
digest_size = new().digest_size
|
||||
|
||||
#: The internal block size of the hash algorithm in bytes.
|
||||
block_size = new().block_size
|
26
modules/Crypto/Hash/RIPEMD.py
Normal file
26
modules/Crypto/Hash/RIPEMD.py
Normal file
@ -0,0 +1,26 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# ===================================================================
|
||||
# The contents of this file are dedicated to the public domain. To
|
||||
# the extent that dedication to the public domain is not available,
|
||||
# everyone is granted a worldwide, perpetual, royalty-free,
|
||||
# non-exclusive license to exercise all rights associated with the
|
||||
# contents of this file for any purpose whatsoever.
|
||||
# No rights are reserved.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
# SOFTWARE.
|
||||
# ===================================================================
|
||||
|
||||
# This file exists for backward compatibility with old code that refers to
|
||||
# Crypto.Hash.RIPEMD
|
||||
|
||||
"""Deprecated alias for `Crypto.Hash.RIPEMD160`"""
|
||||
|
||||
from Crypto.Hash.RIPEMD160 import new, block_size, digest_size
|
24
modules/Crypto/Hash/SHA.py
Normal file
24
modules/Crypto/Hash/SHA.py
Normal file
@ -0,0 +1,24 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# ===================================================================
|
||||
# The contents of this file are dedicated to the public domain. To
|
||||
# the extent that dedication to the public domain is not available,
|
||||
# everyone is granted a worldwide, perpetual, royalty-free,
|
||||
# non-exclusive license to exercise all rights associated with the
|
||||
# contents of this file for any purpose whatsoever.
|
||||
# No rights are reserved.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
# SOFTWARE.
|
||||
# ===================================================================
|
||||
|
||||
# This file exists for backward compatibility with old code that refers to
|
||||
# Crypto.Hash.SHA
|
||||
|
||||
from Crypto.Hash.SHA1 import __doc__, new, block_size, digest_size
|
92
modules/Crypto/Hash/SHA1.py
Normal file
92
modules/Crypto/Hash/SHA1.py
Normal file
@ -0,0 +1,92 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# ===================================================================
|
||||
# The contents of this file are dedicated to the public domain. To
|
||||
# the extent that dedication to the public domain is not available,
|
||||
# everyone is granted a worldwide, perpetual, royalty-free,
|
||||
# non-exclusive license to exercise all rights associated with the
|
||||
# contents of this file for any purpose whatsoever.
|
||||
# No rights are reserved.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
# SOFTWARE.
|
||||
# ===================================================================
|
||||
|
||||
"""SHA-1 cryptographic hash algorithm.
|
||||
|
||||
SHA-1_ produces the 160 bit digest of a message.
|
||||
|
||||
>>> from Crypto.Hash import SHA1
|
||||
>>>
|
||||
>>> h = SHA1.new()
|
||||
>>> h.update(b'Hello')
|
||||
>>> print h.hexdigest()
|
||||
|
||||
*SHA* stands for Secure Hash Algorithm.
|
||||
|
||||
This algorithm is not considered secure. Do not use it for new designs.
|
||||
|
||||
.. _SHA-1: http://csrc.nist.gov/publications/fips/fips180-2/fips180-2.pdf
|
||||
"""
|
||||
|
||||
from __future__ import nested_scopes
|
||||
|
||||
_revision__ = "$Id$"
|
||||
|
||||
__all__ = ['new', 'block_size', 'digest_size']
|
||||
|
||||
from Crypto.Util.py3compat import *
|
||||
if sys.version_info[0] == 2 and sys.version_info[1] == 1:
|
||||
from Crypto.Util.py21compat import *
|
||||
|
||||
def __make_constructor():
|
||||
try:
|
||||
# The sha module is deprecated in Python 2.6, so use hashlib when possible.
|
||||
from hashlib import sha1 as _hash_new
|
||||
except ImportError:
|
||||
from sha import new as _hash_new
|
||||
|
||||
h = _hash_new()
|
||||
if hasattr(h, 'new') and hasattr(h, 'name') and hasattr(h, 'digest_size') and hasattr(h, 'block_size'):
|
||||
# The module from stdlib has the API that we need. Just use it.
|
||||
return _hash_new
|
||||
else:
|
||||
# Wrap the hash object in something that gives us the expected API.
|
||||
_copy_sentinel = object()
|
||||
class _SHA1(object):
|
||||
digest_size = 20
|
||||
block_size = 64
|
||||
name = "sha1"
|
||||
def __init__(self, *args):
|
||||
if args and args[0] is _copy_sentinel:
|
||||
self._h = args[1]
|
||||
else:
|
||||
self._h = _hash_new(*args)
|
||||
def copy(self):
|
||||
return _SHA1(_copy_sentinel, self._h.copy())
|
||||
def update(self, *args):
|
||||
f = self.update = self._h.update
|
||||
f(*args)
|
||||
def digest(self):
|
||||
f = self.digest = self._h.digest
|
||||
return f()
|
||||
def hexdigest(self):
|
||||
f = self.hexdigest = self._h.hexdigest
|
||||
return f()
|
||||
_SHA1.new = _SHA1
|
||||
return _SHA1
|
||||
|
||||
new = __make_constructor()
|
||||
del __make_constructor
|
||||
|
||||
#: The size of the resulting hash in bytes.
|
||||
digest_size = new().digest_size
|
||||
|
||||
#: The internal block size of the hash algorithm in bytes.
|
||||
block_size = new().block_size
|
176
modules/Crypto/Hash/__init__.py
Normal file
176
modules/Crypto/Hash/__init__.py
Normal file
@ -0,0 +1,176 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# ===================================================================
|
||||
# The contents of this file are dedicated to the public domain. To
|
||||
# the extent that dedication to the public domain is not available,
|
||||
# everyone is granted a worldwide, perpetual, royalty-free,
|
||||
# non-exclusive license to exercise all rights associated with the
|
||||
# contents of this file for any purpose whatsoever.
|
||||
# No rights are reserved.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
# SOFTWARE.
|
||||
# ===================================================================
|
||||
|
||||
"""Hashing algorithms
|
||||
|
||||
Hash functions take arbitrary binary strings as input, and produce a random-like output
|
||||
of fixed size that is dependent on the input; it should be practically infeasible
|
||||
to derive the original input data given only the hash function's
|
||||
output. In other words, the hash function is *one-way*.
|
||||
|
||||
It should also not be practically feasible to find a second piece of data
|
||||
(a *second pre-image*) whose hash is the same as the original message
|
||||
(*weak collision resistance*).
|
||||
|
||||
Finally, it should not be feasible to find two arbitrary messages with the
|
||||
same hash (*strong collision resistance*).
|
||||
|
||||
The output of the hash function is called the *digest* of the input message.
|
||||
In general, the security of a hash function is related to the length of the
|
||||
digest. If the digest is *n* bits long, its security level is roughly comparable
|
||||
to the the one offered by an *n/2* bit encryption algorithm.
|
||||
|
||||
Hash functions can be used simply as a integrity check, or, in
|
||||
association with a public-key algorithm, can be used to implement
|
||||
digital signatures.
|
||||
|
||||
The hashing modules here all support the interface described in `PEP
|
||||
247`_ , "API for Cryptographic Hash Functions".
|
||||
|
||||
.. _`PEP 247` : http://www.python.org/dev/peps/pep-0247/
|
||||
|
||||
:undocumented: _MD2, _MD4, _RIPEMD160, _SHA224, _SHA256, _SHA384, _SHA512
|
||||
"""
|
||||
|
||||
__all__ = ['HMAC', 'MD2', 'MD4', 'MD5', 'RIPEMD160', 'SHA1',
|
||||
'SHA224', 'SHA256', 'SHA384', 'SHA512', 'CMAC']
|
||||
|
||||
__revision__ = "$Id$"
|
||||
|
||||
import sys
|
||||
if sys.version_info[0] == 2 and sys.version_info[1] == 1:
|
||||
from Crypto.Util.py21compat import *
|
||||
from Crypto.Util.py3compat import *
|
||||
|
||||
def new(algo, *args):
|
||||
"""Initialize a new hash object.
|
||||
|
||||
The first argument to this function may be an algorithm name or another
|
||||
hash object.
|
||||
|
||||
This function has significant overhead. It's recommended that you instead
|
||||
import and use the individual hash modules directly.
|
||||
"""
|
||||
|
||||
# Try just invoking algo.new()
|
||||
# We do this first so that this is the fastest.
|
||||
try:
|
||||
new_func = algo.new
|
||||
except AttributeError:
|
||||
pass
|
||||
else:
|
||||
return new_func(*args)
|
||||
|
||||
# Try getting the algorithm name.
|
||||
if isinstance(algo, str):
|
||||
name = algo
|
||||
else:
|
||||
try:
|
||||
name = algo.name
|
||||
except AttributeError:
|
||||
raise ValueError("unsupported hash type %r" % (algo,))
|
||||
|
||||
# Got the name. Let's see if we have a PyCrypto implementation.
|
||||
try:
|
||||
new_func = _new_funcs[name]
|
||||
except KeyError:
|
||||
# No PyCrypto implementation. Try hashlib.
|
||||
try:
|
||||
import hashlib
|
||||
except ImportError:
|
||||
# There is no hashlib.
|
||||
raise ValueError("unsupported hash type %s" % (name,))
|
||||
return hashlib.new(name, *args)
|
||||
else:
|
||||
# We have a PyCrypto implementation. Instantiate it.
|
||||
return new_func(*args)
|
||||
|
||||
# This dict originally gets the following _*_new methods, but its members get
|
||||
# replaced with the real new() methods of the various hash modules as they are
|
||||
# used. We do it without locks to improve performance, which is safe in
|
||||
# CPython because dict access is atomic in CPython. This might break PyPI.
|
||||
_new_funcs = {}
|
||||
|
||||
def _md2_new(*args):
|
||||
from Crypto.Hash import MD2
|
||||
_new_funcs['MD2'] = _new_funcs['md2'] = MD2.new
|
||||
return MD2.new(*args)
|
||||
_new_funcs['MD2'] = _new_funcs['md2'] = _md2_new
|
||||
del _md2_new
|
||||
|
||||
def _md4_new(*args):
|
||||
from Crypto.Hash import MD4
|
||||
_new_funcs['MD4'] = _new_funcs['md4'] = MD4.new
|
||||
return MD4.new(*args)
|
||||
_new_funcs['MD4'] = _new_funcs['md4'] = _md4_new
|
||||
del _md4_new
|
||||
|
||||
def _md5_new(*args):
|
||||
from Crypto.Hash import MD5
|
||||
_new_funcs['MD5'] = _new_funcs['md5'] = MD5.new
|
||||
return MD5.new(*args)
|
||||
_new_funcs['MD5'] = _new_funcs['md5'] = _md5_new
|
||||
del _md5_new
|
||||
|
||||
def _ripemd160_new(*args):
|
||||
from Crypto.Hash import RIPEMD160
|
||||
_new_funcs['RIPEMD160'] = _new_funcs['ripemd160'] = \
|
||||
_new_funcs['RIPEMD'] = _new_funcs['ripemd'] = RIPEMD160.new
|
||||
return RIPEMD160.new(*args)
|
||||
_new_funcs['RIPEMD160'] = _new_funcs['ripemd160'] = \
|
||||
_new_funcs['RIPEMD'] = _new_funcs['ripemd'] = _ripemd160_new
|
||||
del _ripemd160_new
|
||||
|
||||
def _sha1_new(*args):
|
||||
from Crypto.Hash import SHA1
|
||||
_new_funcs['SHA1'] = _new_funcs['sha1'] = \
|
||||
_new_funcs['SHA'] = _new_funcs['sha'] = SHA1.new
|
||||
return SHA1.new(*args)
|
||||
_new_funcs['SHA1'] = _new_funcs['sha1'] = \
|
||||
_new_funcs['SHA'] = _new_funcs['sha'] = _sha1_new
|
||||
del _sha1_new
|
||||
|
||||
def _sha224_new(*args):
|
||||
from Crypto.Hash import SHA224
|
||||
_new_funcs['SHA224'] = _new_funcs['sha224'] = SHA224.new
|
||||
return SHA224.new(*args)
|
||||
_new_funcs['SHA224'] = _new_funcs['sha224'] = _sha224_new
|
||||
del _sha224_new
|
||||
|
||||
def _sha256_new(*args):
|
||||
from Crypto.Hash import SHA256
|
||||
_new_funcs['SHA256'] = _new_funcs['sha256'] = SHA256.new
|
||||
return SHA256.new(*args)
|
||||
_new_funcs['SHA256'] = _new_funcs['sha256'] = _sha256_new
|
||||
del _sha256_new
|
||||
|
||||
def _sha384_new(*args):
|
||||
from Crypto.Hash import SHA384
|
||||
_new_funcs['SHA384'] = _new_funcs['sha384'] = SHA384.new
|
||||
return SHA384.new(*args)
|
||||
_new_funcs['SHA384'] = _new_funcs['sha384'] = _sha384_new
|
||||
del _sha384_new
|
||||
|
||||
def _sha512_new(*args):
|
||||
from Crypto.Hash import SHA512
|
||||
_new_funcs['SHA512'] = _new_funcs['sha512'] = SHA512.new
|
||||
return SHA512.new(*args)
|
||||
_new_funcs['SHA512'] = _new_funcs['sha512'] = _sha512_new
|
||||
del _sha512_new
|
163
modules/Crypto/IO/PEM.py
Normal file
163
modules/Crypto/IO/PEM.py
Normal file
@ -0,0 +1,163 @@
|
||||
# -*- coding: ascii -*-
|
||||
#
|
||||
# Util/PEM.py : Privacy Enhanced Mail utilities
|
||||
#
|
||||
# ===================================================================
|
||||
# The contents of this file are dedicated to the public domain. To
|
||||
# the extent that dedication to the public domain is not available,
|
||||
# everyone is granted a worldwide, perpetual, royalty-free,
|
||||
# non-exclusive license to exercise all rights associated with the
|
||||
# contents of this file for any purpose whatsoever.
|
||||
# No rights are reserved.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
# SOFTWARE.
|
||||
# ===================================================================
|
||||
"""Set of functions for encapsulating data according to the PEM format.
|
||||
|
||||
PEM (Privacy Enhanced Mail) was an IETF standard for securing emails via a
|
||||
Public Key Infrastructure. It is specified in RFC 1421-1424.
|
||||
|
||||
Even though it has been abandoned, the simple message encapsulation it defined
|
||||
is still widely used today for encoding *binary* cryptographic objects like
|
||||
keys and certificates into text.
|
||||
"""
|
||||
|
||||
__all__ = ['encode', 'decode']
|
||||
|
||||
import sys
|
||||
if sys.version_info[0] == 2 and sys.version_info[1] == 1:
|
||||
from Crypto.Util.py21compat import *
|
||||
from Crypto.Util.py3compat import *
|
||||
|
||||
import re
|
||||
from binascii import hexlify, unhexlify, a2b_base64, b2a_base64
|
||||
|
||||
from Crypto.Hash import MD5
|
||||
from Crypto.Util.Padding import pad, unpad
|
||||
from Crypto.Cipher import DES, DES3, AES
|
||||
from Crypto.Protocol.KDF import PBKDF1
|
||||
from Crypto.Random import get_random_bytes
|
||||
|
||||
|
||||
def encode(data, marker, passphrase=None, randfunc=None):
|
||||
"""Encode a piece of binary data into PEM format.
|
||||
|
||||
:Parameters:
|
||||
data : byte string
|
||||
The piece of binary data to encode.
|
||||
marker : string
|
||||
The marker for the PEM block (e.g. "PUBLIC KEY").
|
||||
Note that there is no official master list for all allowed markers.
|
||||
Still, you can refer to the OpenSSL_ source code.
|
||||
passphrase : byte string
|
||||
If given, the PEM block will be encrypted. The key is derived from
|
||||
the passphrase.
|
||||
randfunc : callable
|
||||
Random number generation function; it accepts an integer N and returns
|
||||
a byte string of random data, N bytes long. If not given, a new one is
|
||||
instantiated.
|
||||
:Returns:
|
||||
The PEM block, as a string.
|
||||
|
||||
.. _OpenSSL: http://cvs.openssl.org/fileview?f=openssl/crypto/pem/pem.h&v=1.66.2.1.4.2
|
||||
"""
|
||||
|
||||
if randfunc is None:
|
||||
randfunc = get_random_bytes
|
||||
|
||||
out = "-----BEGIN %s-----\n" % marker
|
||||
if passphrase:
|
||||
# We only support 3DES for encryption
|
||||
salt = randfunc(8)
|
||||
key = PBKDF1(passphrase, salt, 16, 1, MD5)
|
||||
key += PBKDF1(key + passphrase, salt, 8, 1, MD5)
|
||||
objenc = DES3.new(key, DES3.MODE_CBC, salt)
|
||||
out += "Proc-Type: 4,ENCRYPTED\nDEK-Info: DES-EDE3-CBC,%s\n\n" %\
|
||||
tostr(hexlify(salt).upper())
|
||||
# Encrypt with PKCS#7 padding
|
||||
data = objenc.encrypt(pad(data, objenc.block_size))
|
||||
|
||||
# Each BASE64 line can take up to 64 characters (=48 bytes of data)
|
||||
# b2a_base64 adds a new line character!
|
||||
chunks = [tostr(b2a_base64(data[i:i + 48]))
|
||||
for i in range(0, len(data), 48)]
|
||||
out += "".join(chunks)
|
||||
out += "-----END %s-----" % marker
|
||||
return out
|
||||
|
||||
|
||||
def decode(pem_data, passphrase=None):
|
||||
"""Decode a PEM block into binary.
|
||||
|
||||
:Parameters:
|
||||
pem_data : string
|
||||
The PEM block.
|
||||
passphrase : byte string
|
||||
If given and the PEM block is encrypted,
|
||||
the key will be derived from the passphrase.
|
||||
:Returns:
|
||||
A tuple with the binary data, the marker string, and a boolean to
|
||||
indicate if decryption was performed.
|
||||
:Raises ValueError:
|
||||
If decoding fails, if the PEM file is encrypted and no passphrase has
|
||||
been provided or if the passphrase is incorrect.
|
||||
"""
|
||||
|
||||
# Verify Pre-Encapsulation Boundary
|
||||
r = re.compile("\s*-----BEGIN (.*)-----\n")
|
||||
m = r.match(pem_data)
|
||||
if not m:
|
||||
raise ValueError("Not a valid PEM pre boundary")
|
||||
marker = m.group(1)
|
||||
|
||||
# Verify Post-Encapsulation Boundary
|
||||
r = re.compile("-----END (.*)-----\s*$")
|
||||
m = r.search(pem_data)
|
||||
if not m or m.group(1) != marker:
|
||||
raise ValueError("Not a valid PEM post boundary")
|
||||
|
||||
# Removes spaces and slit on lines
|
||||
lines = pem_data.replace(" ", '').split()
|
||||
|
||||
# Decrypts, if necessary
|
||||
if lines[1].startswith('Proc-Type:4,ENCRYPTED'):
|
||||
if not passphrase:
|
||||
raise ValueError("PEM is encrypted, but no passphrase available")
|
||||
DEK = lines[2].split(':')
|
||||
if len(DEK) != 2 or DEK[0] != 'DEK-Info':
|
||||
raise ValueError("PEM encryption format not supported.")
|
||||
algo, salt = DEK[1].split(',')
|
||||
salt = unhexlify(tobytes(salt))
|
||||
if algo == "DES-CBC":
|
||||
# This is EVP_BytesToKey in OpenSSL
|
||||
key = PBKDF1(passphrase, salt, 8, 1, MD5)
|
||||
objdec = DES.new(key, DES.MODE_CBC, salt)
|
||||
elif algo == "DES-EDE3-CBC":
|
||||
# Note that EVP_BytesToKey is note exactly the same as PBKDF1
|
||||
key = PBKDF1(passphrase, salt, 16, 1, MD5)
|
||||
key += PBKDF1(key + passphrase, salt, 8, 1, MD5)
|
||||
objdec = DES3.new(key, DES3.MODE_CBC, salt)
|
||||
elif algo == "AES-128-CBC":
|
||||
key = PBKDF1(passphrase, salt[:8], 16, 1, MD5)
|
||||
objdec = AES.new(key, AES.MODE_CBC, salt)
|
||||
else:
|
||||
raise ValueError("Unsupport PEM encryption algorithm.")
|
||||
lines = lines[2:]
|
||||
else:
|
||||
objdec = None
|
||||
|
||||
# Decode body
|
||||
data = a2b_base64(b(''.join(lines[1:-1])))
|
||||
enc_flag = False
|
||||
if objdec:
|
||||
data = unpad(objdec.decrypt(data), objdec.block_size)
|
||||
enc_flag = True
|
||||
|
||||
return (data, marker, enc_flag)
|
209
modules/Crypto/IO/PKCS8.py
Normal file
209
modules/Crypto/IO/PKCS8.py
Normal file
@ -0,0 +1,209 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# PublicKey/PKCS8.py : PKCS#8 functions
|
||||
#
|
||||
# ===================================================================
|
||||
# The contents of this file are dedicated to the public domain. To
|
||||
# the extent that dedication to the public domain is not available,
|
||||
# everyone is granted a worldwide, perpetual, royalty-free,
|
||||
# non-exclusive license to exercise all rights associated with the
|
||||
# contents of this file for any purpose whatsoever.
|
||||
# No rights are reserved.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
# SOFTWARE.
|
||||
# ===================================================================
|
||||
"""
|
||||
Module for handling private keys wrapped according to `PKCS#8`_.
|
||||
|
||||
PKCS8 is a standard for storing and transferring private key information.
|
||||
The wrapped key can either be clear or encrypted.
|
||||
|
||||
All encryption algorithms are based on passphrase-based key derivation.
|
||||
The following mechanisms are fully supported:
|
||||
|
||||
* *PBKDF2WithHMAC-SHA1AndAES128-CBC*
|
||||
* *PBKDF2WithHMAC-SHA1AndAES192-CBC*
|
||||
* *PBKDF2WithHMAC-SHA1AndAES256-CBC*
|
||||
* *PBKDF2WithHMAC-SHA1AndDES-EDE3-CBC*
|
||||
|
||||
The following mechanisms are only supported for importing keys.
|
||||
They are much weaker than the ones listed above, and they are provided
|
||||
for backward compatibility only:
|
||||
|
||||
* *pbeWithMD5AndRC2-CBC*
|
||||
* *pbeWithMD5AndDES-CBC*
|
||||
* *pbeWithSHA1AndRC2-CBC*
|
||||
* *pbeWithSHA1AndDES-CBC*
|
||||
|
||||
.. _`PKCS#8`: http://www.ietf.org/rfc/rfc5208.txt
|
||||
|
||||
"""
|
||||
|
||||
import sys
|
||||
|
||||
if sys.version_info[0] == 2 and sys.version_info[1] == 1:
|
||||
from Crypto.Util.py21compat import *
|
||||
from Crypto.Util.py3compat import *
|
||||
|
||||
from Crypto.Util.asn1 import *
|
||||
|
||||
from Crypto.IO._PBES import PBES1, PBES2
|
||||
|
||||
__all__ = ['wrap', 'unwrap']
|
||||
|
||||
|
||||
def decode_der(obj_class, binstr):
|
||||
"""Instantiate a DER object class, decode a DER binary string in it, and
|
||||
return the object."""
|
||||
der = obj_class()
|
||||
der.decode(binstr)
|
||||
return der
|
||||
|
||||
|
||||
def wrap(private_key, key_oid, passphrase=None, protection=None,
|
||||
prot_params=None, key_params=None, randfunc=None):
|
||||
"""Wrap a private key into a PKCS#8 blob (clear or encrypted).
|
||||
|
||||
:Parameters:
|
||||
|
||||
private_key : byte string
|
||||
The private key encoded in binary form. The actual encoding is
|
||||
algorithm specific. In most cases, it is DER.
|
||||
|
||||
key_oid : string
|
||||
The object identifier (OID) of the private key to wrap.
|
||||
It is a dotted string, like "``1.2.840.113549.1.1.1``" (for RSA keys).
|
||||
|
||||
passphrase : (binary) string
|
||||
The secret passphrase from which the wrapping key is derived.
|
||||
Set it only if encryption is required.
|
||||
|
||||
protection : string
|
||||
The identifier of the algorithm to use for securely wrapping the key.
|
||||
The default value is '``PBKDF2WithHMAC-SHA1AndDES-EDE3-CBC``'.
|
||||
|
||||
prot_params : dictionary
|
||||
Parameters for the protection algorithm.
|
||||
|
||||
+------------------+-----------------------------------------------+
|
||||
| Key | Description |
|
||||
+==================+===============================================+
|
||||
| iteration_count | The KDF algorithm is repeated several times to|
|
||||
| | slow down brute force attacks on passwords. |
|
||||
| | The default value is 1 000. |
|
||||
+------------------+-----------------------------------------------+
|
||||
| salt_size | Salt is used to thwart dictionary and rainbow |
|
||||
| | attacks on passwords. The default value is 8 |
|
||||
| | bytes. |
|
||||
+------------------+-----------------------------------------------+
|
||||
|
||||
key_params : DER object
|
||||
The algorithm parameters associated to the private key.
|
||||
It is required for algorithms like DSA, but not for others like RSA.
|
||||
|
||||
randfunc : callable
|
||||
Random number generation function; it should accept a single integer
|
||||
N and return a string of random data, N bytes long.
|
||||
If not specified, a new RNG will be instantiated
|
||||
from ``Crypto.Random``.
|
||||
|
||||
:Return:
|
||||
The PKCS#8-wrapped private key (possibly encrypted),
|
||||
as a binary string.
|
||||
"""
|
||||
|
||||
if key_params is None:
|
||||
key_params = DerNull()
|
||||
|
||||
#
|
||||
# PrivateKeyInfo ::= SEQUENCE {
|
||||
# version Version,
|
||||
# privateKeyAlgorithm PrivateKeyAlgorithmIdentifier,
|
||||
# privateKey PrivateKey,
|
||||
# attributes [0] IMPLICIT Attributes OPTIONAL
|
||||
# }
|
||||
#
|
||||
pk_info = newDerSequence(
|
||||
0,
|
||||
newDerSequence(
|
||||
DerObjectId(key_oid),
|
||||
key_params
|
||||
),
|
||||
newDerOctetString(private_key)
|
||||
)
|
||||
pk_info_der = pk_info.encode()
|
||||
|
||||
if not passphrase:
|
||||
return pk_info_der
|
||||
|
||||
# Encryption with PBES2
|
||||
passphrase = tobytes(passphrase)
|
||||
if protection is None:
|
||||
protection = 'PBKDF2WithHMAC-SHA1AndDES-EDE3-CBC'
|
||||
return PBES2.encrypt(pk_info_der, passphrase,
|
||||
protection, prot_params, randfunc)
|
||||
|
||||
|
||||
def unwrap(p8_private_key, passphrase=None):
|
||||
"""Unwrap a private key from a PKCS#8 blob (clear or encrypted).
|
||||
|
||||
:Parameters:
|
||||
p8_private_key : byte string
|
||||
The private key wrapped into a PKCS#8 blob
|
||||
passphrase : (byte) string
|
||||
The passphrase to use to decrypt the blob (if it is encrypted).
|
||||
:Return:
|
||||
A tuple containing:
|
||||
|
||||
#. the algorithm identifier of the wrapped key (OID, dotted string)
|
||||
#. the private key (byte string, DER encoded)
|
||||
#. the associated parameters (byte string, DER encoded) or ``None``
|
||||
|
||||
:Raises ValueError:
|
||||
If decoding fails
|
||||
"""
|
||||
|
||||
if passphrase:
|
||||
passphrase = tobytes(passphrase)
|
||||
found = False
|
||||
for pbes in PBES1, PBES2:
|
||||
try:
|
||||
p8_private_key = pbes.decrypt(p8_private_key, passphrase)
|
||||
except ValueError:
|
||||
pass
|
||||
else:
|
||||
found = True
|
||||
break
|
||||
if not found:
|
||||
raise ValueError("Unsupported PKCS#5 Object ID ")
|
||||
|
||||
pk_info = decode_der(DerSequence, p8_private_key)
|
||||
if len(pk_info) == 2 and not passphrase:
|
||||
raise ValueError("Not a valid clear PKCS#8 structure "
|
||||
"(maybe it is encrypted?)")
|
||||
if not 3 <= len(pk_info) <= 4 or pk_info[0] != 0:
|
||||
raise ValueError("Not a valid PrivateKeyInfo SEQUENCE")
|
||||
|
||||
#
|
||||
# AlgorithmIdentifier ::= SEQUENCE {
|
||||
# algorithm OBJECT IDENTIFIER,
|
||||
# parameters ANY DEFINED BY algorithm OPTIONAL
|
||||
# }
|
||||
#
|
||||
algo_id = decode_der(DerSequence, pk_info[1])
|
||||
if not 1 <= len(algo_id) <= 2:
|
||||
raise ValueError("Not a valid AlgorithmIdentifier SEQUENCE")
|
||||
algo = decode_der(DerObjectId, algo_id[0]).value
|
||||
private_key = decode_der(DerOctetString, pk_info[2]).payload
|
||||
if len(algo_id) == 2 and algo_id[1] != b('\x05\x00'):
|
||||
params = algo_id[1]
|
||||
else:
|
||||
params = None
|
||||
return (algo, private_key, params)
|
348
modules/Crypto/IO/_PBES.py
Normal file
348
modules/Crypto/IO/_PBES.py
Normal file
@ -0,0 +1,348 @@
|
||||
#
|
||||
# PublicKey/_PBES.py : Password-Based Encryption functions
|
||||
#
|
||||
# ===================================================================
|
||||
# The contents of this file are dedicated to the public domain. To
|
||||
# the extent that dedication to the public domain is not available,
|
||||
# everyone is granted a worldwide, perpetual, royalty-free,
|
||||
# non-exclusive license to exercise all rights associated with the
|
||||
# contents of this file for any purpose whatsoever.
|
||||
# No rights are reserved.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
# SOFTWARE.
|
||||
# ===================================================================
|
||||
|
||||
import sys
|
||||
if sys.version_info[0] == 2 and sys.version_info[1] == 1:
|
||||
from Crypto.Util.py21compat import *
|
||||
from Crypto.Util.py3compat import *
|
||||
|
||||
from Crypto import Random
|
||||
from Crypto.Util.asn1 import *
|
||||
|
||||
from Crypto.Util.Padding import pad, unpad
|
||||
from Crypto.Hash import MD5, SHA1
|
||||
from Crypto.Cipher import DES, ARC2, DES3, AES
|
||||
from Crypto.Protocol.KDF import PBKDF1, PBKDF2
|
||||
|
||||
|
||||
# These are the ASN.1 definitions used by the PBES1/2 logic:
|
||||
#
|
||||
# EncryptedPrivateKeyInfo ::= SEQUENCE {
|
||||
# encryptionAlgorithm EncryptionAlgorithmIdentifier,
|
||||
# encryptedData EncryptedData
|
||||
# }
|
||||
#
|
||||
# EncryptionAlgorithmIdentifier ::= AlgorithmIdentifier
|
||||
#
|
||||
# EncryptedData ::= OCTET STRING
|
||||
#
|
||||
# AlgorithmIdentifier ::= SEQUENCE {
|
||||
# algorithm OBJECT IDENTIFIER,
|
||||
# parameters ANY DEFINED BY algorithm OPTIONAL
|
||||
# }
|
||||
#
|
||||
# PBEParameter ::= SEQUENCE {
|
||||
# salt OCTET STRING (SIZE(8)),
|
||||
# iterationCount INTEGER
|
||||
# }
|
||||
#
|
||||
# PBES2-params ::= SEQUENCE {
|
||||
# keyDerivationFunc AlgorithmIdentifier {{PBES2-KDFs}},
|
||||
# encryptionScheme AlgorithmIdentifier {{PBES2-Encs}}
|
||||
# }
|
||||
#
|
||||
# PBKDF2-params ::= SEQUENCE {
|
||||
# salt CHOICE {
|
||||
# specified OCTET STRING,
|
||||
# otherSource AlgorithmIdentifier {{PBKDF2-SaltSources}}
|
||||
# },
|
||||
# iterationCount INTEGER (1..MAX),
|
||||
# keyLength INTEGER (1..MAX) OPTIONAL,
|
||||
# prf AlgorithmIdentifier {{PBKDF2-PRFs}} DEFAULT algid-hmacWithSHA1
|
||||
# }
|
||||
#
|
||||
|
||||
|
||||
def decode_der(obj_class, binstr):
|
||||
"""Instantiate a DER object class, decode a DER binary string in it, and
|
||||
return the object."""
|
||||
der = obj_class()
|
||||
der.decode(binstr)
|
||||
return der
|
||||
|
||||
|
||||
class PBES1(object):
|
||||
"""Deprecated encryption scheme with password-based key derivation
|
||||
(originally defined in PKCS#5 v1.5, but still present in `v2.0`__).
|
||||
|
||||
.. __: http://www.ietf.org/rfc/rfc2898.txt
|
||||
"""
|
||||
|
||||
def decrypt(data, passphrase):
|
||||
"""Decrypt a piece of data using a passphrase and *PBES1*.
|
||||
|
||||
The algorithm to use is automatically detected.
|
||||
|
||||
:Parameters:
|
||||
data : byte string
|
||||
The piece of data to decrypt.
|
||||
passphrase : byte string
|
||||
The passphrase to use for decrypting the data.
|
||||
:Returns:
|
||||
The decrypted data, as a binary string.
|
||||
"""
|
||||
|
||||
encrypted_private_key_info = decode_der(DerSequence, data)
|
||||
encrypted_algorithm = decode_der(
|
||||
DerSequence,
|
||||
encrypted_private_key_info[0]
|
||||
)
|
||||
encrypted_data = decode_der(
|
||||
DerOctetString,
|
||||
encrypted_private_key_info[1]
|
||||
).payload
|
||||
|
||||
pbe_oid = decode_der(DerObjectId, encrypted_algorithm[0]).value
|
||||
cipher_params = {}
|
||||
if pbe_oid == "1.2.840.113549.1.5.3":
|
||||
# PBE_MD5_DES_CBC
|
||||
hashmod = MD5
|
||||
ciphermod = DES
|
||||
elif pbe_oid == "1.2.840.113549.1.5.6":
|
||||
# PBE_MD5_RC2_CBC
|
||||
hashmod = MD5
|
||||
ciphermod = ARC2
|
||||
cipher_params['effective_keylen'] = 64
|
||||
elif pbe_oid == "1.2.840.113549.1.5.10":
|
||||
# PBE_SHA1_DES_CBC
|
||||
hashmod = SHA1
|
||||
ciphermod = DES
|
||||
elif pbe_oid == "1.2.840.113549.1.5.11":
|
||||
# PBE_SHA1_RC2_CBC
|
||||
hashmod = SHA1
|
||||
ciphermod = ARC2
|
||||
cipher_params['effective_keylen'] = 64
|
||||
else:
|
||||
raise ValueError("Unknown OID")
|
||||
|
||||
pbe_params = decode_der(DerSequence, encrypted_algorithm[1])
|
||||
salt = decode_der(DerOctetString, pbe_params[0]).payload
|
||||
iterations = pbe_params[1]
|
||||
|
||||
key_iv = PBKDF1(passphrase, salt, 16, iterations, hashmod)
|
||||
key, iv = key_iv[:8], key_iv[8:]
|
||||
|
||||
cipher = ciphermod.new(key, ciphermod.MODE_CBC, iv, **cipher_params)
|
||||
pt = cipher.decrypt(encrypted_data)
|
||||
return unpad(pt, cipher.block_size)
|
||||
decrypt = staticmethod(decrypt)
|
||||
|
||||
|
||||
class PBES2(object):
|
||||
"""Encryption scheme with password-based key derivation
|
||||
(defined in `PKCS#5 v2.0`__).
|
||||
|
||||
.. __: http://www.ietf.org/rfc/rfc2898.txt."""
|
||||
|
||||
def encrypt(data, passphrase, protection, prot_params=None, randfunc=None):
|
||||
"""Encrypt a piece of data using a passphrase and *PBES2*.
|
||||
|
||||
:Parameters:
|
||||
data : byte string
|
||||
The piece of data to encrypt.
|
||||
passphrase : byte string
|
||||
The passphrase to use for encrypting the data.
|
||||
protection : string
|
||||
The identifier of the encryption algorithm to use.
|
||||
The default value is '``PBKDF2WithHMAC-SHA1AndDES-EDE3-CBC``'.
|
||||
prot_params : dictionary
|
||||
Parameters of the protection algorithm.
|
||||
|
||||
+------------------+-----------------------------------------------+
|
||||
| Key | Description |
|
||||
+==================+===============================================+
|
||||
| iteration_count | The KDF algorithm is repeated several times to|
|
||||
| | slow down brute force attacks on passwords. |
|
||||
| | The default value is 1 000. |
|
||||
+------------------+-----------------------------------------------+
|
||||
| salt_size | Salt is used to thwart dictionary and rainbow |
|
||||
| | attacks on passwords. The default value is 8 |
|
||||
| | bytes. |
|
||||
+------------------+-----------------------------------------------+
|
||||
|
||||
randfunc : callable
|
||||
Random number generation function; it should accept
|
||||
a single integer N and return a string of random data,
|
||||
N bytes long. If not specified, a new RNG will be
|
||||
instantiated from ``Crypto.Random``.
|
||||
|
||||
:Returns:
|
||||
The encrypted data, as a binary string.
|
||||
"""
|
||||
|
||||
if prot_params is None:
|
||||
prot_params = {}
|
||||
|
||||
if randfunc is None:
|
||||
randfunc = Random.new().read
|
||||
|
||||
if protection == 'PBKDF2WithHMAC-SHA1AndDES-EDE3-CBC':
|
||||
key_size = 24
|
||||
module = DES3
|
||||
protection = DES3.MODE_CBC
|
||||
enc_oid = "1.2.840.113549.3.7"
|
||||
elif protection == 'PBKDF2WithHMAC-SHA1AndAES128-CBC':
|
||||
key_size = 16
|
||||
module = AES
|
||||
protection = AES.MODE_CBC
|
||||
enc_oid = "2.16.840.1.101.3.4.1.2"
|
||||
elif protection == 'PBKDF2WithHMAC-SHA1AndAES192-CBC':
|
||||
key_size = 24
|
||||
module = AES
|
||||
protection = AES.MODE_CBC
|
||||
enc_oid = "2.16.840.1.101.3.4.1.22"
|
||||
elif protection == 'PBKDF2WithHMAC-SHA1AndAES256-CBC':
|
||||
key_size = 32
|
||||
module = AES
|
||||
protection = AES.MODE_CBC
|
||||
enc_oid = "2.16.840.1.101.3.4.1.42"
|
||||
else:
|
||||
raise ValueError("Unknown mode")
|
||||
|
||||
# Get random data
|
||||
iv = randfunc(module.block_size)
|
||||
salt = randfunc(prot_params.get("salt_size", 8))
|
||||
|
||||
# Derive key from password
|
||||
count = prot_params.get("iteration_count", 1000)
|
||||
key = PBKDF2(passphrase, salt, key_size, count)
|
||||
key_derivation_func = newDerSequence(
|
||||
DerObjectId("1.2.840.113549.1.5.12"), # PBKDF2
|
||||
newDerSequence(
|
||||
DerOctetString(salt),
|
||||
DerInteger(count)
|
||||
)
|
||||
)
|
||||
|
||||
# Create cipher and use it
|
||||
cipher = module.new(key, protection, iv)
|
||||
encrypted_data = cipher.encrypt(pad(data, cipher.block_size))
|
||||
encryption_scheme = newDerSequence(
|
||||
DerObjectId(enc_oid),
|
||||
DerOctetString(iv)
|
||||
)
|
||||
|
||||
# Result
|
||||
encrypted_private_key_info = newDerSequence(
|
||||
# encryptionAlgorithm
|
||||
newDerSequence(
|
||||
DerObjectId("1.2.840.113549.1.5.13"), # PBES2
|
||||
newDerSequence(
|
||||
key_derivation_func,
|
||||
encryption_scheme
|
||||
),
|
||||
),
|
||||
DerOctetString(encrypted_data)
|
||||
)
|
||||
return encrypted_private_key_info.encode()
|
||||
encrypt = staticmethod(encrypt)
|
||||
|
||||
def decrypt(data, passphrase):
|
||||
"""Decrypt a piece of data using a passphrase and *PBES2*.
|
||||
|
||||
The algorithm to use is automatically detected.
|
||||
|
||||
:Parameters:
|
||||
data : byte string
|
||||
The piece of data to decrypt.
|
||||
passphrase : byte string
|
||||
The passphrase to use for decrypting the data.
|
||||
:Returns:
|
||||
The decrypted data, as a binary string.
|
||||
"""
|
||||
|
||||
encrypted_private_key_info = decode_der(DerSequence, data)
|
||||
encryption_algorithm = decode_der(
|
||||
DerSequence,
|
||||
encrypted_private_key_info[0]
|
||||
)
|
||||
encrypted_data = decode_der(
|
||||
DerOctetString,
|
||||
encrypted_private_key_info[1]
|
||||
).payload
|
||||
|
||||
pbe_oid = decode_der(DerObjectId, encryption_algorithm[0]).value
|
||||
if pbe_oid != "1.2.840.113549.1.5.13":
|
||||
raise ValueError("Not a PBES2 object")
|
||||
|
||||
pbes2_params = decode_der(DerSequence, encryption_algorithm[1])
|
||||
|
||||
### Key Derivation Function selection
|
||||
key_derivation_func = decode_der(DerSequence, pbes2_params[0])
|
||||
key_derivation_oid = decode_der(
|
||||
DerObjectId,
|
||||
key_derivation_func[0]
|
||||
).value
|
||||
|
||||
# For now, we only support PBKDF2
|
||||
if key_derivation_oid != "1.2.840.113549.1.5.12":
|
||||
raise ValueError("Unknown KDF")
|
||||
|
||||
pbkdf2_params = decode_der(DerSequence, key_derivation_func[1])
|
||||
salt = decode_der(DerOctetString, pbkdf2_params[0]).payload
|
||||
iteration_count = pbkdf2_params[1]
|
||||
if len(pbkdf2_params) > 2:
|
||||
pbkdf2_key_length = pbkdf2_params[2]
|
||||
else:
|
||||
pbkdf2_key_length = None
|
||||
if len(pbkdf2_params) > 3:
|
||||
raise ValueError("Unsupported PRF for PBKDF2")
|
||||
|
||||
### Cipher selection
|
||||
encryption_scheme = decode_der(DerSequence, pbes2_params[1])
|
||||
encryption_oid = decode_der(
|
||||
DerObjectId,
|
||||
encryption_scheme[0]
|
||||
).value
|
||||
|
||||
if encryption_oid == "1.2.840.113549.3.7":
|
||||
# DES_EDE3_CBC
|
||||
ciphermod = DES3
|
||||
key_size = 24
|
||||
elif encryption_oid == "2.16.840.1.101.3.4.1.2":
|
||||
# AES128_CBC
|
||||
ciphermod = AES
|
||||
key_size = 16
|
||||
elif encryption_oid == "2.16.840.1.101.3.4.1.22":
|
||||
# AES192_CBC
|
||||
ciphermod = AES
|
||||
key_size = 24
|
||||
elif encryption_oid == "2.16.840.1.101.3.4.1.42":
|
||||
# AES256_CBC
|
||||
ciphermod = AES
|
||||
key_size = 32
|
||||
else:
|
||||
raise ValueError("Unsupported cipher")
|
||||
|
||||
if pbkdf2_key_length and pbkdf2_key_length != key_size:
|
||||
raise ValueError("Mismatch between PBKDF2 parameters"
|
||||
" and selected cipher")
|
||||
|
||||
IV = decode_der(DerOctetString, encryption_scheme[1]).payload
|
||||
|
||||
# Create cipher
|
||||
key = PBKDF2(passphrase, salt, key_size, iteration_count)
|
||||
cipher = ciphermod.new(key, ciphermod.MODE_CBC, IV)
|
||||
|
||||
# Decrypt data
|
||||
pt = cipher.decrypt(encrypted_data)
|
||||
return unpad(pt, cipher.block_size)
|
||||
decrypt = staticmethod(decrypt)
|
32
modules/Crypto/IO/__init__.py
Normal file
32
modules/Crypto/IO/__init__.py
Normal file
@ -0,0 +1,32 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# ===================================================================
|
||||
# The contents of this file are dedicated to the public domain. To
|
||||
# the extent that dedication to the public domain is not available,
|
||||
# everyone is granted a worldwide, perpetual, royalty-free,
|
||||
# non-exclusive license to exercise all rights associated with the
|
||||
# contents of this file for any purpose whatsoever.
|
||||
# No rights are reserved.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
# SOFTWARE.
|
||||
# ===================================================================
|
||||
|
||||
"""
|
||||
Modules for reading and writing cryptographic data.
|
||||
|
||||
======================== =============================================
|
||||
Module Description
|
||||
======================== =============================================
|
||||
Crypto.Util.PEM Set of functions for encapsulating data according to the PEM format.
|
||||
Crypto.Util.PKCS8 Set of functions for wrapping/unwrapping private keys.
|
||||
======================== =============================================
|
||||
"""
|
||||
|
||||
__all__ = ['PEM', 'PKCS8']
|
319
modules/Crypto/Protocol/AllOrNothing.py
Normal file
319
modules/Crypto/Protocol/AllOrNothing.py
Normal file
@ -0,0 +1,319 @@
|
||||
#
|
||||
# AllOrNothing.py : all-or-nothing package transformations
|
||||
#
|
||||
# Part of the Python Cryptography Toolkit
|
||||
#
|
||||
# Written by Andrew M. Kuchling and others
|
||||
#
|
||||
# ===================================================================
|
||||
# The contents of this file are dedicated to the public domain. To
|
||||
# the extent that dedication to the public domain is not available,
|
||||
# everyone is granted a worldwide, perpetual, royalty-free,
|
||||
# non-exclusive license to exercise all rights associated with the
|
||||
# contents of this file for any purpose whatsoever.
|
||||
# No rights are reserved.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
# SOFTWARE.
|
||||
# ===================================================================
|
||||
|
||||
"""This file implements all-or-nothing package transformations.
|
||||
|
||||
An all-or-nothing package transformation is one in which some text is
|
||||
transformed into message blocks, such that all blocks must be obtained before
|
||||
the reverse transformation can be applied. Thus, if any blocks are corrupted
|
||||
or lost, the original message cannot be reproduced.
|
||||
|
||||
An all-or-nothing package transformation is not encryption, although a block
|
||||
cipher algorithm is used. The encryption key is randomly generated and is
|
||||
extractable from the message blocks.
|
||||
|
||||
This class implements the All-Or-Nothing package transformation algorithm
|
||||
described in:
|
||||
|
||||
Ronald L. Rivest. "All-Or-Nothing Encryption and The Package Transform"
|
||||
http://theory.lcs.mit.edu/~rivest/fusion.pdf
|
||||
|
||||
"""
|
||||
|
||||
__revision__ = "$Id$"
|
||||
|
||||
import operator
|
||||
import sys
|
||||
from Crypto.Util.number import bytes_to_long, long_to_bytes
|
||||
from Crypto.Util.py3compat import *
|
||||
|
||||
def isInt(x):
|
||||
test = 0
|
||||
try:
|
||||
test += x
|
||||
except TypeError:
|
||||
return 0
|
||||
return 1
|
||||
|
||||
class AllOrNothing:
|
||||
"""Class implementing the All-or-Nothing package transform.
|
||||
|
||||
Methods for subclassing:
|
||||
|
||||
_inventkey(key_size):
|
||||
Returns a randomly generated key. Subclasses can use this to
|
||||
implement better random key generating algorithms. The default
|
||||
algorithm is probably not very cryptographically secure.
|
||||
|
||||
"""
|
||||
|
||||
def __init__(self, ciphermodule, mode=None, IV=None):
|
||||
"""AllOrNothing(ciphermodule, mode=None, IV=None)
|
||||
|
||||
ciphermodule is a module implementing the cipher algorithm to
|
||||
use. It must provide the PEP272 interface.
|
||||
|
||||
Note that the encryption key is randomly generated
|
||||
automatically when needed. Optional arguments mode and IV are
|
||||
passed directly through to the ciphermodule.new() method; they
|
||||
are the feedback mode and initialization vector to use. All
|
||||
three arguments must be the same for the object used to create
|
||||
the digest, and to undigest'ify the message blocks.
|
||||
"""
|
||||
|
||||
self.__ciphermodule = ciphermodule
|
||||
self.__mode = mode
|
||||
self.__IV = IV
|
||||
self.__key_size = ciphermodule.key_size
|
||||
if not isInt(self.__key_size) or self.__key_size==0:
|
||||
self.__key_size = 16
|
||||
|
||||
__K0digit = bchr(0x69)
|
||||
|
||||
def digest(self, text):
|
||||
"""digest(text:string) : [string]
|
||||
|
||||
Perform the All-or-Nothing package transform on the given
|
||||
string. Output is a list of message blocks describing the
|
||||
transformed text, where each block is a string of bit length equal
|
||||
to the ciphermodule's block_size.
|
||||
"""
|
||||
|
||||
# generate a random session key and K0, the key used to encrypt the
|
||||
# hash blocks. Rivest calls this a fixed, publically-known encryption
|
||||
# key, but says nothing about the security implications of this key or
|
||||
# how to choose it.
|
||||
key = self._inventkey(self.__key_size)
|
||||
K0 = self.__K0digit * self.__key_size
|
||||
|
||||
# we need two cipher objects here, one that is used to encrypt the
|
||||
# message blocks and one that is used to encrypt the hashes. The
|
||||
# former uses the randomly generated key, while the latter uses the
|
||||
# well-known key.
|
||||
mcipher = self.__newcipher(key)
|
||||
hcipher = self.__newcipher(K0)
|
||||
|
||||
# Pad the text so that its length is a multiple of the cipher's
|
||||
# block_size. Pad with trailing spaces, which will be eliminated in
|
||||
# the undigest() step.
|
||||
block_size = self.__ciphermodule.block_size
|
||||
padbytes = block_size - (len(text) % block_size)
|
||||
text = text + b(' ') * padbytes
|
||||
|
||||
# Run through the algorithm:
|
||||
# s: number of message blocks (size of text / block_size)
|
||||
# input sequence: m1, m2, ... ms
|
||||
# random key K' (`key' in the code)
|
||||
# Compute output sequence: m'1, m'2, ... m's' for s' = s + 1
|
||||
# Let m'i = mi ^ E(K', i) for i = 1, 2, 3, ..., s
|
||||
# Let m's' = K' ^ h1 ^ h2 ^ ... hs
|
||||
# where hi = E(K0, m'i ^ i) for i = 1, 2, ... s
|
||||
#
|
||||
# The one complication I add is that the last message block is hard
|
||||
# coded to the number of padbytes added, so that these can be stripped
|
||||
# during the undigest() step
|
||||
s = divmod(len(text), block_size)[0]
|
||||
blocks = []
|
||||
hashes = []
|
||||
for i in range(1, s+1):
|
||||
start = (i-1) * block_size
|
||||
end = start + block_size
|
||||
mi = text[start:end]
|
||||
assert len(mi) == block_size
|
||||
cipherblock = mcipher.encrypt(long_to_bytes(i, block_size))
|
||||
mticki = bytes_to_long(mi) ^ bytes_to_long(cipherblock)
|
||||
blocks.append(mticki)
|
||||
# calculate the hash block for this block
|
||||
hi = hcipher.encrypt(long_to_bytes(mticki ^ i, block_size))
|
||||
hashes.append(bytes_to_long(hi))
|
||||
|
||||
# Add the padbytes length as a message block
|
||||
i = i + 1
|
||||
cipherblock = mcipher.encrypt(long_to_bytes(i, block_size))
|
||||
mticki = padbytes ^ bytes_to_long(cipherblock)
|
||||
blocks.append(mticki)
|
||||
|
||||
# calculate this block's hash
|
||||
hi = hcipher.encrypt(long_to_bytes(mticki ^ i, block_size))
|
||||
hashes.append(bytes_to_long(hi))
|
||||
|
||||
# Now calculate the last message block of the sequence 1..s'. This
|
||||
# will contain the random session key XOR'd with all the hash blocks,
|
||||
# so that for undigest(), once all the hash blocks are calculated, the
|
||||
# session key can be trivially extracted. Calculating all the hash
|
||||
# blocks requires that all the message blocks be received, thus the
|
||||
# All-or-Nothing algorithm succeeds.
|
||||
mtick_stick = bytes_to_long(key) ^ reduce(operator.xor, hashes)
|
||||
blocks.append(mtick_stick)
|
||||
|
||||
# we convert the blocks to strings since in Python, byte sequences are
|
||||
# always represented as strings. This is more consistent with the
|
||||
# model that encryption and hash algorithms always operate on strings.
|
||||
return [long_to_bytes(i,self.__ciphermodule.block_size) for i in blocks]
|
||||
|
||||
|
||||
def undigest(self, blocks):
|
||||
"""undigest(blocks : [string]) : string
|
||||
|
||||
Perform the reverse package transformation on a list of message
|
||||
blocks. Note that the ciphermodule used for both transformations
|
||||
must be the same. blocks is a list of strings of bit length
|
||||
equal to the ciphermodule's block_size.
|
||||
"""
|
||||
|
||||
# better have at least 2 blocks, for the padbytes package and the hash
|
||||
# block accumulator
|
||||
if len(blocks) < 2:
|
||||
raise ValueError, "List must be at least length 2."
|
||||
|
||||
# blocks is a list of strings. We need to deal with them as long
|
||||
# integers
|
||||
blocks = map(bytes_to_long, blocks)
|
||||
|
||||
# Calculate the well-known key, to which the hash blocks are
|
||||
# encrypted, and create the hash cipher.
|
||||
K0 = self.__K0digit * self.__key_size
|
||||
hcipher = self.__newcipher(K0)
|
||||
block_size = self.__ciphermodule.block_size
|
||||
|
||||
# Since we have all the blocks (or this method would have been called
|
||||
# prematurely), we can calculate all the hash blocks.
|
||||
hashes = []
|
||||
for i in range(1, len(blocks)):
|
||||
mticki = blocks[i-1] ^ i
|
||||
hi = hcipher.encrypt(long_to_bytes(mticki, block_size))
|
||||
hashes.append(bytes_to_long(hi))
|
||||
|
||||
# now we can calculate K' (key). remember the last block contains
|
||||
# m's' which we don't include here
|
||||
key = blocks[-1] ^ reduce(operator.xor, hashes)
|
||||
|
||||
# and now we can create the cipher object
|
||||
mcipher = self.__newcipher(long_to_bytes(key, self.__key_size))
|
||||
|
||||
# And we can now decode the original message blocks
|
||||
parts = []
|
||||
for i in range(1, len(blocks)):
|
||||
cipherblock = mcipher.encrypt(long_to_bytes(i, block_size))
|
||||
mi = blocks[i-1] ^ bytes_to_long(cipherblock)
|
||||
parts.append(mi)
|
||||
|
||||
# The last message block contains the number of pad bytes appended to
|
||||
# the original text string, such that its length was an even multiple
|
||||
# of the cipher's block_size. This number should be small enough that
|
||||
# the conversion from long integer to integer should never overflow
|
||||
padbytes = int(parts[-1])
|
||||
text = b('').join(map(long_to_bytes, parts[:-1]))
|
||||
return text[:-padbytes]
|
||||
|
||||
def _inventkey(self, key_size):
|
||||
# Return key_size random bytes
|
||||
from Crypto import Random
|
||||
return Random.new().read(key_size)
|
||||
|
||||
def __newcipher(self, key):
|
||||
if self.__mode is None and self.__IV is None:
|
||||
return self.__ciphermodule.new(key)
|
||||
elif self.__IV is None:
|
||||
return self.__ciphermodule.new(key, self.__mode)
|
||||
else:
|
||||
return self.__ciphermodule.new(key, self.__mode, self.__IV)
|
||||
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
import sys
|
||||
import getopt
|
||||
import base64
|
||||
|
||||
usagemsg = '''\
|
||||
Test module usage: %(program)s [-c cipher] [-l] [-h]
|
||||
|
||||
Where:
|
||||
--cipher module
|
||||
-c module
|
||||
Cipher module to use. Default: %(ciphermodule)s
|
||||
|
||||
--aslong
|
||||
-l
|
||||
Print the encoded message blocks as long integers instead of base64
|
||||
encoded strings
|
||||
|
||||
--help
|
||||
-h
|
||||
Print this help message
|
||||
'''
|
||||
|
||||
ciphermodule = 'AES'
|
||||
aslong = 0
|
||||
|
||||
def usage(code, msg=None):
|
||||
if msg:
|
||||
print msg
|
||||
print usagemsg % {'program': sys.argv[0],
|
||||
'ciphermodule': ciphermodule}
|
||||
sys.exit(code)
|
||||
|
||||
try:
|
||||
opts, args = getopt.getopt(sys.argv[1:],
|
||||
'c:l', ['cipher=', 'aslong'])
|
||||
except getopt.error, msg:
|
||||
usage(1, msg)
|
||||
|
||||
if args:
|
||||
usage(1, 'Too many arguments')
|
||||
|
||||
for opt, arg in opts:
|
||||
if opt in ('-h', '--help'):
|
||||
usage(0)
|
||||
elif opt in ('-c', '--cipher'):
|
||||
ciphermodule = arg
|
||||
elif opt in ('-l', '--aslong'):
|
||||
aslong = 1
|
||||
|
||||
# ugly hack to force __import__ to give us the end-path module
|
||||
module = __import__('Crypto.Cipher.'+ciphermodule, None, None, ['new'])
|
||||
|
||||
x = AllOrNothing(module)
|
||||
print 'Original text:\n=========='
|
||||
print __doc__
|
||||
print '=========='
|
||||
msgblocks = x.digest(b(__doc__))
|
||||
print 'message blocks:'
|
||||
for i, blk in zip(range(len(msgblocks)), msgblocks):
|
||||
# base64 adds a trailing newline
|
||||
print ' %3d' % i,
|
||||
if aslong:
|
||||
print bytes_to_long(blk)
|
||||
else:
|
||||
print base64.encodestring(blk)[:-1]
|
||||
#
|
||||
# get a new undigest-only object so there's no leakage
|
||||
y = AllOrNothing(module)
|
||||
text = y.undigest(msgblocks)
|
||||
if text == b(__doc__):
|
||||
print 'They match!'
|
||||
else:
|
||||
print 'They differ!'
|
245
modules/Crypto/Protocol/Chaffing.py
Normal file
245
modules/Crypto/Protocol/Chaffing.py
Normal file
@ -0,0 +1,245 @@
|
||||
#
|
||||
# Chaffing.py : chaffing & winnowing support
|
||||
#
|
||||
# Part of the Python Cryptography Toolkit
|
||||
#
|
||||
# Written by Andrew M. Kuchling, Barry A. Warsaw, and others
|
||||
#
|
||||
# ===================================================================
|
||||
# The contents of this file are dedicated to the public domain. To
|
||||
# the extent that dedication to the public domain is not available,
|
||||
# everyone is granted a worldwide, perpetual, royalty-free,
|
||||
# non-exclusive license to exercise all rights associated with the
|
||||
# contents of this file for any purpose whatsoever.
|
||||
# No rights are reserved.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
# SOFTWARE.
|
||||
# ===================================================================
|
||||
#
|
||||
"""This file implements the chaffing algorithm.
|
||||
|
||||
Winnowing and chaffing is a technique for enhancing privacy without requiring
|
||||
strong encryption. In short, the technique takes a set of authenticated
|
||||
message blocks (the wheat) and adds a number of chaff blocks which have
|
||||
randomly chosen data and MAC fields. This means that to an adversary, the
|
||||
chaff blocks look as valid as the wheat blocks, and so the authentication
|
||||
would have to be performed on every block. By tailoring the number of chaff
|
||||
blocks added to the message, the sender can make breaking the message
|
||||
computationally infeasible. There are many other interesting properties of
|
||||
the winnow/chaff technique.
|
||||
|
||||
For example, say Alice is sending a message to Bob. She packetizes the
|
||||
message and performs an all-or-nothing transformation on the packets. Then
|
||||
she authenticates each packet with a message authentication code (MAC). The
|
||||
MAC is a hash of the data packet, and there is a secret key which she must
|
||||
share with Bob (key distribution is an exercise left to the reader). She then
|
||||
adds a serial number to each packet, and sends the packets to Bob.
|
||||
|
||||
Bob receives the packets, and using the shared secret authentication key,
|
||||
authenticates the MACs for each packet. Those packets that have bad MACs are
|
||||
simply discarded. The remainder are sorted by serial number, and passed
|
||||
through the reverse all-or-nothing transform. The transform means that an
|
||||
eavesdropper (say Eve) must acquire all the packets before any of the data can
|
||||
be read. If even one packet is missing, the data is useless.
|
||||
|
||||
There's one twist: by adding chaff packets, Alice and Bob can make Eve's job
|
||||
much harder, since Eve now has to break the shared secret key, or try every
|
||||
combination of wheat and chaff packet to read any of the message. The cool
|
||||
thing is that Bob doesn't need to add any additional code; the chaff packets
|
||||
are already filtered out because their MACs don't match (in all likelihood --
|
||||
since the data and MACs for the chaff packets are randomly chosen it is
|
||||
possible, but very unlikely that a chaff MAC will match the chaff data). And
|
||||
Alice need not even be the party adding the chaff! She could be completely
|
||||
unaware that a third party, say Charles, is adding chaff packets to her
|
||||
messages as they are transmitted.
|
||||
|
||||
For more information on winnowing and chaffing see this paper:
|
||||
|
||||
Ronald L. Rivest, "Chaffing and Winnowing: Confidentiality without Encryption"
|
||||
http://theory.lcs.mit.edu/~rivest/chaffing.txt
|
||||
|
||||
"""
|
||||
|
||||
__revision__ = "$Id$"
|
||||
|
||||
from Crypto.Util.number import bytes_to_long
|
||||
|
||||
class Chaff:
|
||||
"""Class implementing the chaff adding algorithm.
|
||||
|
||||
Methods for subclasses:
|
||||
|
||||
_randnum(size):
|
||||
Returns a randomly generated number with a byte-length equal
|
||||
to size. Subclasses can use this to implement better random
|
||||
data and MAC generating algorithms. The default algorithm is
|
||||
probably not very cryptographically secure. It is most
|
||||
important that the chaff data does not contain any patterns
|
||||
that can be used to discern it from wheat data without running
|
||||
the MAC.
|
||||
|
||||
"""
|
||||
|
||||
def __init__(self, factor=1.0, blocksper=1):
|
||||
"""Chaff(factor:float, blocksper:int)
|
||||
|
||||
factor is the number of message blocks to add chaff to,
|
||||
expressed as a percentage between 0.0 and 1.0. blocksper is
|
||||
the number of chaff blocks to include for each block being
|
||||
chaffed. Thus the defaults add one chaff block to every
|
||||
message block. By changing the defaults, you can adjust how
|
||||
computationally difficult it could be for an adversary to
|
||||
brute-force crack the message. The difficulty is expressed
|
||||
as:
|
||||
|
||||
pow(blocksper, int(factor * number-of-blocks))
|
||||
|
||||
For ease of implementation, when factor < 1.0, only the first
|
||||
int(factor*number-of-blocks) message blocks are chaffed.
|
||||
"""
|
||||
|
||||
if not (0.0<=factor<=1.0):
|
||||
raise ValueError, "'factor' must be between 0.0 and 1.0"
|
||||
if blocksper < 0:
|
||||
raise ValueError, "'blocksper' must be zero or more"
|
||||
|
||||
self.__factor = factor
|
||||
self.__blocksper = blocksper
|
||||
|
||||
|
||||
def chaff(self, blocks):
|
||||
"""chaff( [(serial-number:int, data:string, MAC:string)] )
|
||||
: [(int, string, string)]
|
||||
|
||||
Add chaff to message blocks. blocks is a list of 3-tuples of the
|
||||
form (serial-number, data, MAC).
|
||||
|
||||
Chaff is created by choosing a random number of the same
|
||||
byte-length as data, and another random number of the same
|
||||
byte-length as MAC. The message block's serial number is
|
||||
placed on the chaff block and all the packet's chaff blocks
|
||||
are randomly interspersed with the single wheat block. This
|
||||
method then returns a list of 3-tuples of the same form.
|
||||
Chaffed blocks will contain multiple instances of 3-tuples
|
||||
with the same serial number, but the only way to figure out
|
||||
which blocks are wheat and which are chaff is to perform the
|
||||
MAC hash and compare values.
|
||||
"""
|
||||
|
||||
chaffedblocks = []
|
||||
|
||||
# count is the number of blocks to add chaff to. blocksper is the
|
||||
# number of chaff blocks to add per message block that is being
|
||||
# chaffed.
|
||||
count = len(blocks) * self.__factor
|
||||
blocksper = range(self.__blocksper)
|
||||
for i, wheat in zip(range(len(blocks)), blocks):
|
||||
# it shouldn't matter which of the n blocks we add chaff to, so for
|
||||
# ease of implementation, we'll just add them to the first count
|
||||
# blocks
|
||||
if i < count:
|
||||
serial, data, mac = wheat
|
||||
datasize = len(data)
|
||||
macsize = len(mac)
|
||||
addwheat = 1
|
||||
# add chaff to this block
|
||||
for j in blocksper:
|
||||
import sys
|
||||
chaffdata = self._randnum(datasize)
|
||||
chaffmac = self._randnum(macsize)
|
||||
chaff = (serial, chaffdata, chaffmac)
|
||||
# mix up the order, if the 5th bit is on then put the
|
||||
# wheat on the list
|
||||
if addwheat and bytes_to_long(self._randnum(16)) & 0x40:
|
||||
chaffedblocks.append(wheat)
|
||||
addwheat = 0
|
||||
chaffedblocks.append(chaff)
|
||||
if addwheat:
|
||||
chaffedblocks.append(wheat)
|
||||
else:
|
||||
# just add the wheat
|
||||
chaffedblocks.append(wheat)
|
||||
return chaffedblocks
|
||||
|
||||
def _randnum(self, size):
|
||||
from Crypto import Random
|
||||
return Random.new().read(size)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
text = """\
|
||||
We hold these truths to be self-evident, that all men are created equal, that
|
||||
they are endowed by their Creator with certain unalienable Rights, that among
|
||||
these are Life, Liberty, and the pursuit of Happiness. That to secure these
|
||||
rights, Governments are instituted among Men, deriving their just powers from
|
||||
the consent of the governed. That whenever any Form of Government becomes
|
||||
destructive of these ends, it is the Right of the People to alter or to
|
||||
abolish it, and to institute new Government, laying its foundation on such
|
||||
principles and organizing its powers in such form, as to them shall seem most
|
||||
likely to effect their Safety and Happiness.
|
||||
"""
|
||||
print 'Original text:\n=========='
|
||||
print text
|
||||
print '=========='
|
||||
|
||||
# first transform the text into packets
|
||||
blocks = [] ; size = 40
|
||||
for i in range(0, len(text), size):
|
||||
blocks.append( text[i:i+size] )
|
||||
|
||||
# now get MACs for all the text blocks. The key is obvious...
|
||||
print 'Calculating MACs...'
|
||||
from Crypto.Hash import HMAC, SHA
|
||||
key = 'Jefferson'
|
||||
macs = [HMAC.new(key, block, digestmod=SHA).digest()
|
||||
for block in blocks]
|
||||
|
||||
assert len(blocks) == len(macs)
|
||||
|
||||
# put these into a form acceptable as input to the chaffing procedure
|
||||
source = []
|
||||
m = zip(range(len(blocks)), blocks, macs)
|
||||
print m
|
||||
for i, data, mac in m:
|
||||
source.append((i, data, mac))
|
||||
|
||||
# now chaff these
|
||||
print 'Adding chaff...'
|
||||
c = Chaff(factor=0.5, blocksper=2)
|
||||
chaffed = c.chaff(source)
|
||||
|
||||
from base64 import encodestring
|
||||
|
||||
# print the chaffed message blocks. meanwhile, separate the wheat from
|
||||
# the chaff
|
||||
|
||||
wheat = []
|
||||
print 'chaffed message blocks:'
|
||||
for i, data, mac in chaffed:
|
||||
# do the authentication
|
||||
h = HMAC.new(key, data, digestmod=SHA)
|
||||
pmac = h.digest()
|
||||
if pmac == mac:
|
||||
tag = '-->'
|
||||
wheat.append(data)
|
||||
else:
|
||||
tag = ' '
|
||||
# base64 adds a trailing newline
|
||||
print tag, '%3d' % i, \
|
||||
repr(data), encodestring(mac)[:-1]
|
||||
|
||||
# now decode the message packets and check it against the original text
|
||||
print 'Undigesting wheat...'
|
||||
# PY3K: This is meant to be text, do not change to bytes (data)
|
||||
newtext = "".join(wheat)
|
||||
if newtext == text:
|
||||
print 'They match!'
|
||||
else:
|
||||
print 'They differ!'
|
209
modules/Crypto/Protocol/KDF.py
Normal file
209
modules/Crypto/Protocol/KDF.py
Normal file
@ -0,0 +1,209 @@
|
||||
#
|
||||
# KDF.py : a collection of Key Derivation Functions
|
||||
#
|
||||
# Part of the Python Cryptography Toolkit
|
||||
#
|
||||
# ===================================================================
|
||||
# The contents of this file are dedicated to the public domain. To
|
||||
# the extent that dedication to the public domain is not available,
|
||||
# everyone is granted a worldwide, perpetual, royalty-free,
|
||||
# non-exclusive license to exercise all rights associated with the
|
||||
# contents of this file for any purpose whatsoever.
|
||||
# No rights are reserved.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
# SOFTWARE.
|
||||
# ===================================================================
|
||||
|
||||
"""This file contains a collection of standard key derivation functions.
|
||||
|
||||
A key derivation function derives one or more secondary secret keys from
|
||||
one primary secret (a master key or a pass phrase).
|
||||
|
||||
This is typically done to insulate the secondary keys from each other,
|
||||
to avoid that leakage of a secondary key compromises the security of the
|
||||
master key, or to thwart attacks on pass phrases (e.g. via rainbow tables).
|
||||
|
||||
:undocumented: __revision__
|
||||
"""
|
||||
|
||||
__revision__ = "$Id$"
|
||||
|
||||
import math
|
||||
import struct
|
||||
|
||||
import sys
|
||||
if sys.version_info[0] == 2 and sys.version_info[1] == 1:
|
||||
from Crypto.Util.py21compat import *
|
||||
from Crypto.Util.py3compat import *
|
||||
|
||||
from Crypto.Hash import SHA1, HMAC, CMAC
|
||||
from Crypto.Util.strxor import strxor
|
||||
from Crypto.Util.number import long_to_bytes, bytes_to_long
|
||||
|
||||
def PBKDF1(password, salt, dkLen, count=1000, hashAlgo=None):
|
||||
"""Derive one key from a password (or passphrase).
|
||||
|
||||
This function performs key derivation according an old version of
|
||||
the PKCS#5 standard (v1.5).
|
||||
|
||||
This algorithm is called ``PBKDF1``. Even though it is still described
|
||||
in the latest version of the PKCS#5 standard (version 2, or RFC2898),
|
||||
newer applications should use the more secure and versatile `PBKDF2` instead.
|
||||
|
||||
:Parameters:
|
||||
password : string
|
||||
The secret password or pass phrase to generate the key from.
|
||||
salt : byte string
|
||||
An 8 byte string to use for better protection from dictionary attacks.
|
||||
This value does not need to be kept secret, but it should be randomly
|
||||
chosen for each derivation.
|
||||
dkLen : integer
|
||||
The length of the desired key. Default is 16 bytes, suitable for instance for `Crypto.Cipher.AES`.
|
||||
count : integer
|
||||
The number of iterations to carry out. It's recommended to use at least 1000.
|
||||
hashAlgo : module
|
||||
The hash algorithm to use, as a module or an object from the `Crypto.Hash` package.
|
||||
The digest length must be no shorter than ``dkLen``.
|
||||
The default algorithm is `SHA1`.
|
||||
|
||||
:Return: A byte string of length `dkLen` that can be used as key.
|
||||
"""
|
||||
if not hashAlgo:
|
||||
hashAlgo = SHA1
|
||||
password = tobytes(password)
|
||||
pHash = hashAlgo.new(password+salt)
|
||||
digest = pHash.digest_size
|
||||
if dkLen>digest:
|
||||
raise TypeError("Selected hash algorithm has a too short digest (%d bytes)." % digest)
|
||||
if len(salt)!=8:
|
||||
raise ValueError("Salt is not 8 bytes long.")
|
||||
for i in xrange(count-1):
|
||||
pHash = pHash.new(pHash.digest())
|
||||
return pHash.digest()[:dkLen]
|
||||
|
||||
def PBKDF2(password, salt, dkLen=16, count=1000, prf=None):
|
||||
"""Derive one or more keys from a password (or passphrase).
|
||||
|
||||
This performs key derivation according to the PKCS#5 standard (v2.0),
|
||||
by means of the ``PBKDF2`` algorithm.
|
||||
|
||||
:Parameters:
|
||||
password : string
|
||||
The secret password or pass phrase to generate the key from.
|
||||
salt : string
|
||||
A string to use for better protection from dictionary attacks.
|
||||
This value does not need to be kept secret, but it should be randomly
|
||||
chosen for each derivation. It is recommended to be at least 8 bytes long.
|
||||
dkLen : integer
|
||||
The cumulative length of the desired keys. Default is 16 bytes, suitable for instance for `Crypto.Cipher.AES`.
|
||||
count : integer
|
||||
The number of iterations to carry out. It's recommended to use at least 1000.
|
||||
prf : callable
|
||||
A pseudorandom function. It must be a function that returns a pseudorandom string
|
||||
from two parameters: a secret and a salt. If not specified, HMAC-SHA1 is used.
|
||||
|
||||
:Return: A byte string of length `dkLen` that can be used as key material.
|
||||
If you wanted multiple keys, just break up this string into segments of the desired length.
|
||||
"""
|
||||
password = tobytes(password)
|
||||
if prf is None:
|
||||
prf = lambda p,s: HMAC.new(p,s,SHA1).digest()
|
||||
key = b('')
|
||||
i = 1
|
||||
while len(key)<dkLen:
|
||||
U = previousU = prf(password,salt+struct.pack(">I", i))
|
||||
for j in xrange(count-1):
|
||||
previousU = t = prf(password,previousU)
|
||||
U = strxor(U,t)
|
||||
key += U
|
||||
i = i + 1
|
||||
return key[:dkLen]
|
||||
|
||||
class _S2V(object):
|
||||
"""String-to-vector PRF as defined in `RFC5297`_.
|
||||
|
||||
This class implements a pseudorandom function family
|
||||
based on CMAC that takes as input a vector of strings.
|
||||
|
||||
.. _RFC5297: http://tools.ietf.org/html/rfc5297
|
||||
"""
|
||||
|
||||
def __init__(self, key, ciphermod):
|
||||
"""Initialize the S2V PRF.
|
||||
|
||||
:Parameters:
|
||||
key : byte string
|
||||
A secret that can be used as key for CMACs
|
||||
based on ciphers from ``ciphermod``.
|
||||
ciphermod : module
|
||||
A block cipher module from `Crypto.Cipher`.
|
||||
"""
|
||||
|
||||
self._key = key
|
||||
self._ciphermod = ciphermod
|
||||
self._last_string = self._cache = bchr(0)*ciphermod.block_size
|
||||
self._n_updates = ciphermod.block_size*8-1
|
||||
|
||||
def new(key, ciphermod):
|
||||
"""Create a new S2V PRF.
|
||||
|
||||
:Parameters:
|
||||
key : byte string
|
||||
A secret that can be used as key for CMACs
|
||||
based on ciphers from ``ciphermod``.
|
||||
ciphermod : module
|
||||
A block cipher module from `Crypto.Cipher`.
|
||||
"""
|
||||
return _S2V(key, ciphermod)
|
||||
new = staticmethod(new)
|
||||
|
||||
def _double(self, bs):
|
||||
doubled = bytes_to_long(bs)<<1
|
||||
if bord(bs[0]) & 0x80:
|
||||
doubled ^= 0x87
|
||||
return long_to_bytes(doubled, len(bs))[-len(bs):]
|
||||
|
||||
def update(self, item):
|
||||
"""Pass the next component of the vector.
|
||||
|
||||
The maximum number of components you can pass is equal to the block
|
||||
length of the cipher (in bits) minus 1.
|
||||
|
||||
:Parameters:
|
||||
item : byte string
|
||||
The next component of the vector.
|
||||
:Raise TypeError: when the limit on the number of components has been reached.
|
||||
:Raise ValueError: when the component is empty
|
||||
"""
|
||||
|
||||
if not item:
|
||||
raise ValueError("A component cannot be empty")
|
||||
|
||||
if self._n_updates==0:
|
||||
raise TypeError("Too many components passed to S2V")
|
||||
self._n_updates -= 1
|
||||
|
||||
mac = CMAC.new(self._key, msg=self._last_string, ciphermod=self._ciphermod)
|
||||
self._cache = strxor(self._double(self._cache), mac.digest())
|
||||
self._last_string = item
|
||||
|
||||
def derive(self):
|
||||
""""Derive a secret from the vector of components.
|
||||
|
||||
:Return: a byte string, as long as the block length of the cipher.
|
||||
"""
|
||||
|
||||
if len(self._last_string)>=16:
|
||||
final = self._last_string[:-16] + strxor(self._last_string[-16:], self._cache)
|
||||
else:
|
||||
padded = (self._last_string + bchr(0x80)+ bchr(0)*15)[:16]
|
||||
final = strxor(padded, self._double(self._cache))
|
||||
mac = CMAC.new(self._key, msg=final, ciphermod=self._ciphermod)
|
||||
return mac.digest()
|
41
modules/Crypto/Protocol/__init__.py
Normal file
41
modules/Crypto/Protocol/__init__.py
Normal file
@ -0,0 +1,41 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# ===================================================================
|
||||
# The contents of this file are dedicated to the public domain. To
|
||||
# the extent that dedication to the public domain is not available,
|
||||
# everyone is granted a worldwide, perpetual, royalty-free,
|
||||
# non-exclusive license to exercise all rights associated with the
|
||||
# contents of this file for any purpose whatsoever.
|
||||
# No rights are reserved.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
# SOFTWARE.
|
||||
# ===================================================================
|
||||
|
||||
"""Cryptographic protocols
|
||||
|
||||
Implements various cryptographic protocols. (Don't expect to find
|
||||
network protocols here.)
|
||||
|
||||
Crypto.Protocol.AllOrNothing
|
||||
Transforms a message into a set of message blocks, such that the blocks
|
||||
can be recombined to get the message back.
|
||||
|
||||
Crypto.Protocol.Chaffing
|
||||
Takes a set of authenticated message blocks (the wheat) and adds a number
|
||||
of randomly generated blocks (the chaff).
|
||||
|
||||
Crypto.Protocol.KDF
|
||||
A collection of standard key derivation functions.
|
||||
|
||||
:undocumented: __revision__
|
||||
"""
|
||||
|
||||
__all__ = ['AllOrNothing', 'Chaffing', 'KDF']
|
||||
__revision__ = "$Id$"
|
682
modules/Crypto/PublicKey/DSA.py
Normal file
682
modules/Crypto/PublicKey/DSA.py
Normal file
@ -0,0 +1,682 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# PublicKey/DSA.py : DSA signature primitive
|
||||
#
|
||||
# Written in 2008 by Dwayne C. Litzenberger <dlitz@dlitz.net>
|
||||
#
|
||||
# ===================================================================
|
||||
# The contents of this file are dedicated to the public domain. To
|
||||
# the extent that dedication to the public domain is not available,
|
||||
# everyone is granted a worldwide, perpetual, royalty-free,
|
||||
# non-exclusive license to exercise all rights associated with the
|
||||
# contents of this file for any purpose whatsoever.
|
||||
# No rights are reserved.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
# SOFTWARE.
|
||||
# ===================================================================
|
||||
|
||||
"""DSA public-key signature algorithm.
|
||||
|
||||
DSA_ is a widespread public-key signature algorithm. Its security is
|
||||
based on the discrete logarithm problem (DLP_). Given a cyclic
|
||||
group, a generator *g*, and an element *h*, it is hard
|
||||
to find an integer *x* such that *g^x = h*. The problem is believed
|
||||
to be difficult, and it has been proved such (and therefore secure) for
|
||||
more than 30 years.
|
||||
|
||||
The group is actually a sub-group over the integers modulo *p*, with *p* prime.
|
||||
The sub-group order is *q*, which is prime too; it always holds that *(p-1)* is a multiple of *q*.
|
||||
The cryptographic strength is linked to the magnitude of *p* and *q*.
|
||||
The signer holds a value *x* (*0<x<q-1*) as private key, and its public
|
||||
key (*y* where *y=g^x mod p*) is distributed.
|
||||
|
||||
In 2012, a sufficient size is deemed to be 2048 bits for *p* and 256 bits for *q*.
|
||||
For more information, see the most recent ECRYPT_ report.
|
||||
|
||||
DSA is reasonably secure for new designs.
|
||||
|
||||
The algorithm can only be used for authentication (digital signature).
|
||||
DSA cannot be used for confidentiality (encryption).
|
||||
|
||||
The values *(p,q,g)* are called *domain parameters*;
|
||||
they are not sensitive but must be shared by both parties (the signer and the verifier).
|
||||
Different signers can share the same domain parameters with no security
|
||||
concerns.
|
||||
|
||||
The DSA signature is twice as big as the size of *q* (64 bytes if *q* is 256 bit
|
||||
long).
|
||||
|
||||
This module provides facilities for generating new DSA keys and for constructing
|
||||
them from known components. DSA keys allows you to perform basic signing and
|
||||
verification.
|
||||
|
||||
>>> from Crypto.Random import random
|
||||
>>> from Crypto.PublicKey import DSA
|
||||
>>> from Crypto.Hash import SHA256
|
||||
>>>
|
||||
>>> message = "Hello"
|
||||
>>> key = DSA.generate(2048)
|
||||
>>> f = open("public_key.pem", "w")
|
||||
>>> f.write(key.publickey().exportKey(key))
|
||||
>>> h = SHA256.new(message).digest()
|
||||
>>> k = random.StrongRandom().randint(1,key.q-1)
|
||||
>>> sig = key.sign(h,k)
|
||||
>>> ...
|
||||
>>> ...
|
||||
>>> f = open("public_key.pem", "r")
|
||||
>>> h = SHA256.new(message).digest()
|
||||
>>> key = DSA.importKey(f.read())
|
||||
>>> if key.verify(h,sig):
|
||||
>>> print "OK"
|
||||
>>> else:
|
||||
>>> print "Incorrect signature"
|
||||
|
||||
.. _DSA: http://en.wikipedia.org/wiki/Digital_Signature_Algorithm
|
||||
.. _DLP: http://www.cosic.esat.kuleuven.be/publications/talk-78.pdf
|
||||
.. _ECRYPT: http://www.ecrypt.eu.org/documents/D.SPA.17.pdf
|
||||
"""
|
||||
|
||||
__revision__ = "$Id$"
|
||||
|
||||
__all__ = ['generate', 'construct', 'error', 'DSAImplementation',
|
||||
'_DSAobj', 'importKey']
|
||||
|
||||
import binascii
|
||||
import struct
|
||||
|
||||
import sys
|
||||
if sys.version_info[0] == 2 and sys.version_info[1] == 1:
|
||||
from Crypto.Util.py21compat import *
|
||||
from Crypto.Util.py3compat import *
|
||||
|
||||
from Crypto import Random
|
||||
from Crypto.IO import PKCS8, PEM
|
||||
from Crypto.Util.number import bytes_to_long, long_to_bytes
|
||||
from Crypto.PublicKey import _DSA, _slowmath, pubkey, KeyFormatError
|
||||
from Crypto.Util.asn1 import DerObject, DerSequence,\
|
||||
DerInteger, DerObjectId, DerBitString, newDerSequence, newDerBitString
|
||||
|
||||
try:
|
||||
from Crypto.PublicKey import _fastmath
|
||||
except ImportError:
|
||||
_fastmath = None
|
||||
|
||||
def decode_der(obj_class, binstr):
|
||||
"""Instantiate a DER object class, decode a DER binary string in it,
|
||||
and return the object."""
|
||||
der = obj_class()
|
||||
der.decode(binstr)
|
||||
return der
|
||||
|
||||
# ; The following ASN.1 types are relevant for DSA
|
||||
#
|
||||
# SubjectPublicKeyInfo ::= SEQUENCE {
|
||||
# algorithm AlgorithmIdentifier,
|
||||
# subjectPublicKey BIT STRING
|
||||
# }
|
||||
#
|
||||
# id-dsa ID ::= { iso(1) member-body(2) us(840) x9-57(10040) x9cm(4) 1 }
|
||||
#
|
||||
# ; See RFC3279
|
||||
# Dss-Parms ::= SEQUENCE {
|
||||
# p INTEGER,
|
||||
# q INTEGER,
|
||||
# g INTEGER
|
||||
# }
|
||||
#
|
||||
# DSAPublicKey ::= INTEGER
|
||||
#
|
||||
# DSSPrivatKey_OpenSSL ::= SEQUENCE
|
||||
# version INTEGER,
|
||||
# p INTEGER,
|
||||
# q INTEGER,
|
||||
# g INTEGER,
|
||||
# y INTEGER,
|
||||
# x INTEGER
|
||||
# }
|
||||
#
|
||||
|
||||
class _DSAobj(pubkey.pubkey):
|
||||
"""Class defining an actual DSA key.
|
||||
|
||||
:undocumented: __getstate__, __setstate__, __repr__, __getattr__
|
||||
"""
|
||||
#: Dictionary of DSA parameters.
|
||||
#:
|
||||
#: A public key will only have the following entries:
|
||||
#:
|
||||
#: - **y**, the public key.
|
||||
#: - **g**, the generator.
|
||||
#: - **p**, the modulus.
|
||||
#: - **q**, the order of the sub-group.
|
||||
#:
|
||||
#: A private key will also have:
|
||||
#:
|
||||
#: - **x**, the private key.
|
||||
keydata = ['y', 'g', 'p', 'q', 'x']
|
||||
|
||||
def __init__(self, implementation, key, randfunc=None):
|
||||
self.implementation = implementation
|
||||
self.key = key
|
||||
if randfunc is None:
|
||||
randfunc = Random.new().read
|
||||
self._randfunc = randfunc
|
||||
|
||||
def __getattr__(self, attrname):
|
||||
if attrname in self.keydata:
|
||||
# For backward compatibility, allow the user to get (not set) the
|
||||
# DSA key parameters directly from this object.
|
||||
return getattr(self.key, attrname)
|
||||
else:
|
||||
raise AttributeError("%s object has no %r attribute" % (self.__class__.__name__, attrname,))
|
||||
|
||||
def sign(self, M, K):
|
||||
"""Sign a piece of data with DSA.
|
||||
|
||||
:Parameter M: The piece of data to sign with DSA. It may
|
||||
not be longer in bit size than the sub-group order (*q*).
|
||||
:Type M: byte string or long
|
||||
|
||||
:Parameter K: A secret number, chosen randomly in the closed
|
||||
range *[1,q-1]*.
|
||||
:Type K: long (recommended) or byte string (not recommended)
|
||||
|
||||
:attention: selection of *K* is crucial for security. Generating a
|
||||
random number larger than *q* and taking the modulus by *q* is
|
||||
**not** secure, since smaller values will occur more frequently.
|
||||
Generating a random number systematically smaller than *q-1*
|
||||
(e.g. *floor((q-1)/8)* random bytes) is also **not** secure. In general,
|
||||
it shall not be possible for an attacker to know the value of `any
|
||||
bit of K`__.
|
||||
|
||||
:attention: The number *K* shall not be reused for any other
|
||||
operation and shall be discarded immediately.
|
||||
|
||||
:attention: M must be a digest cryptographic hash, otherwise
|
||||
an attacker may mount an existential forgery attack.
|
||||
|
||||
:Return: A tuple with 2 longs.
|
||||
|
||||
.. __: http://www.di.ens.fr/~pnguyen/pub_NgSh00.htm
|
||||
"""
|
||||
return pubkey.pubkey.sign(self, M, K)
|
||||
|
||||
def verify(self, M, signature):
|
||||
"""Verify the validity of a DSA signature.
|
||||
|
||||
:Parameter M: The expected message.
|
||||
:Type M: byte string or long
|
||||
|
||||
:Parameter signature: The DSA signature to verify.
|
||||
:Type signature: A tuple with 2 longs as return by `sign`
|
||||
|
||||
:Return: True if the signature is correct, False otherwise.
|
||||
"""
|
||||
return pubkey.pubkey.verify(self, M, signature)
|
||||
|
||||
def _encrypt(self, c, K):
|
||||
raise TypeError("DSA cannot encrypt")
|
||||
|
||||
def _decrypt(self, c):
|
||||
raise TypeError("DSA cannot decrypt")
|
||||
|
||||
def _blind(self, m, r):
|
||||
raise TypeError("DSA cannot blind")
|
||||
|
||||
def _unblind(self, m, r):
|
||||
raise TypeError("DSA cannot unblind")
|
||||
|
||||
def _sign(self, m, k):
|
||||
return self.key._sign(m, k)
|
||||
|
||||
def _verify(self, m, sig):
|
||||
(r, s) = sig
|
||||
return self.key._verify(m, r, s)
|
||||
|
||||
def has_private(self):
|
||||
return self.key.has_private()
|
||||
|
||||
def size(self):
|
||||
return self.key.size()
|
||||
|
||||
def can_blind(self):
|
||||
return False
|
||||
|
||||
def can_encrypt(self):
|
||||
return False
|
||||
|
||||
def can_sign(self):
|
||||
return True
|
||||
|
||||
def publickey(self):
|
||||
return self.implementation.construct((self.key.y, self.key.g, self.key.p, self.key.q))
|
||||
|
||||
def __getstate__(self):
|
||||
d = {}
|
||||
for k in self.keydata:
|
||||
try:
|
||||
d[k] = getattr(self.key, k)
|
||||
except AttributeError:
|
||||
pass
|
||||
return d
|
||||
|
||||
def __setstate__(self, d):
|
||||
if not hasattr(self, 'implementation'):
|
||||
self.implementation = DSAImplementation()
|
||||
if not hasattr(self, '_randfunc'):
|
||||
self._randfunc = Random.new().read
|
||||
t = []
|
||||
for k in self.keydata:
|
||||
if not d.has_key(k):
|
||||
break
|
||||
t.append(d[k])
|
||||
self.key = self.implementation._math.dsa_construct(*tuple(t))
|
||||
|
||||
def __repr__(self):
|
||||
attrs = []
|
||||
for k in self.keydata:
|
||||
if k == 'p':
|
||||
attrs.append("p(%d)" % (self.size()+1,))
|
||||
elif hasattr(self.key, k):
|
||||
attrs.append(k)
|
||||
if self.has_private():
|
||||
attrs.append("private")
|
||||
# PY3K: This is meant to be text, do not change to bytes (data)
|
||||
return "<%s @0x%x %s>" % (self.__class__.__name__, id(self), ",".join(attrs))
|
||||
|
||||
def exportKey(self, format='PEM', pkcs8=None, passphrase=None,
|
||||
protection=None):
|
||||
"""Export this DSA key.
|
||||
|
||||
:Parameters:
|
||||
format : string
|
||||
The format to use for wrapping the key:
|
||||
|
||||
- *'DER'*. Binary encoding.
|
||||
- *'PEM'*. Textual encoding, done according to `RFC1421`_/
|
||||
`RFC1423`_ (default).
|
||||
- *'OpenSSH'*. Textual encoding, one line of text, see `RFC4253`_.
|
||||
Only suitable for public keys, not private keys.
|
||||
|
||||
passphrase : string
|
||||
For private keys only. The pass phrase to use for deriving
|
||||
the encryption key.
|
||||
|
||||
pkcs8 : boolean
|
||||
For private keys only. If ``True`` (default), the key is arranged
|
||||
according to `PKCS#8`_ and if `False`, according to the custom
|
||||
OpenSSL/OpenSSH encoding.
|
||||
|
||||
protection : string
|
||||
The encryption scheme to use for protecting the private key.
|
||||
It is only meaningful when a pass phrase is present too.
|
||||
|
||||
If ``pkcs8`` takes value ``True``, ``protection`` is the PKCS#8
|
||||
algorithm to use for deriving the secret and encrypting
|
||||
the private DSA key.
|
||||
For a complete list of algorithms, see `Crypto.IO.PKCS8`.
|
||||
The default is *PBKDF2WithHMAC-SHA1AndDES-EDE3-CBC*.
|
||||
|
||||
If ``pkcs8`` is ``False``, the obsolete PEM encryption scheme is
|
||||
used. It is based on MD5 for key derivation, and Triple DES for
|
||||
encryption. Parameter ``protection`` is ignored.
|
||||
|
||||
The combination ``format='DER'`` and ``pkcs8=False`` is not allowed
|
||||
if a passphrase is present.
|
||||
|
||||
:Return: A byte string with the encoded public or private half
|
||||
of the key.
|
||||
:Raise ValueError:
|
||||
When the format is unknown or when you try to encrypt a private
|
||||
key with *DER* format and OpenSSL/OpenSSH.
|
||||
:attention:
|
||||
If you don't provide a pass phrase, the private key will be
|
||||
exported in the clear!
|
||||
|
||||
.. _RFC1421: http://www.ietf.org/rfc/rfc1421.txt
|
||||
.. _RFC1423: http://www.ietf.org/rfc/rfc1423.txt
|
||||
.. _RFC4253: http://www.ietf.org/rfc/rfc4253.txt
|
||||
.. _`PKCS#8`: http://www.ietf.org/rfc/rfc5208.txt
|
||||
"""
|
||||
if passphrase is not None:
|
||||
passphrase = tobytes(passphrase)
|
||||
if format == 'OpenSSH':
|
||||
tup1 = [long_to_bytes(x) for x in (self.p, self.q, self.g, self.y)]
|
||||
|
||||
def func(x):
|
||||
if (bord(x[0]) & 0x80):
|
||||
return bchr(0) + x
|
||||
else:
|
||||
return x
|
||||
|
||||
tup2 = map(func, tup1)
|
||||
keyparts = [b('ssh-dss')] + tup2
|
||||
keystring = b('').join(
|
||||
[struct.pack(">I", len(kp)) + kp for kp in keyparts]
|
||||
)
|
||||
return b('ssh-dss ') + binascii.b2a_base64(keystring)[:-1]
|
||||
|
||||
# DER format is always used, even in case of PEM, which simply
|
||||
# encodes it into BASE64.
|
||||
params = newDerSequence(self.p, self.q, self.g)
|
||||
if self.has_private():
|
||||
if pkcs8 is None:
|
||||
pkcs8 = True
|
||||
if pkcs8:
|
||||
if not protection:
|
||||
protection = 'PBKDF2WithHMAC-SHA1AndDES-EDE3-CBC'
|
||||
private_key = DerInteger(self.x).encode()
|
||||
binary_key = PKCS8.wrap(
|
||||
private_key, oid, passphrase,
|
||||
protection, key_params=params,
|
||||
randfunc=self._randfunc
|
||||
)
|
||||
if passphrase:
|
||||
key_type = 'ENCRYPTED PRIVATE'
|
||||
else:
|
||||
key_type = 'PRIVATE'
|
||||
passphrase = None
|
||||
else:
|
||||
if format != 'PEM' and passphrase:
|
||||
raise ValueError("DSA private key cannot be encrypted")
|
||||
ints = [0, self.p, self.q, self.g, self.y, self.x]
|
||||
binary_key = newDerSequence(*ints).encode()
|
||||
key_type = "DSA PRIVATE"
|
||||
else:
|
||||
if pkcs8:
|
||||
raise ValueError("PKCS#8 is only meaningful for private keys")
|
||||
binary_key = newDerSequence(
|
||||
newDerSequence(DerObjectId(oid), params),
|
||||
newDerBitString(DerInteger(self.y))
|
||||
).encode()
|
||||
key_type = "DSA PUBLIC"
|
||||
|
||||
if format == 'DER':
|
||||
return binary_key
|
||||
if format == 'PEM':
|
||||
pem_str = PEM.encode(
|
||||
binary_key, key_type + " KEY",
|
||||
passphrase, self._randfunc
|
||||
)
|
||||
return tobytes(pem_str)
|
||||
raise ValueError("Unknown key format '%s'. Cannot export the DSA key." % format)
|
||||
|
||||
|
||||
class DSAImplementation(object):
|
||||
"""
|
||||
A DSA key factory.
|
||||
|
||||
This class is only internally used to implement the methods of the
|
||||
`Crypto.PublicKey.DSA` module.
|
||||
"""
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
"""Create a new DSA key factory.
|
||||
|
||||
:Keywords:
|
||||
use_fast_math : bool
|
||||
Specify which mathematic library to use:
|
||||
|
||||
- *None* (default). Use fastest math available.
|
||||
- *True* . Use fast math.
|
||||
- *False* . Use slow math.
|
||||
default_randfunc : callable
|
||||
Specify how to collect random data:
|
||||
|
||||
- *None* (default). Use Random.new().read().
|
||||
- not *None* . Use the specified function directly.
|
||||
:Raise RuntimeError:
|
||||
When **use_fast_math** =True but fast math is not available.
|
||||
"""
|
||||
use_fast_math = kwargs.get('use_fast_math', None)
|
||||
if use_fast_math is None: # Automatic
|
||||
if _fastmath is not None:
|
||||
self._math = _fastmath
|
||||
else:
|
||||
self._math = _slowmath
|
||||
|
||||
elif use_fast_math: # Explicitly select fast math
|
||||
if _fastmath is not None:
|
||||
self._math = _fastmath
|
||||
else:
|
||||
raise RuntimeError("fast math module not available")
|
||||
|
||||
else: # Explicitly select slow math
|
||||
self._math = _slowmath
|
||||
|
||||
self.error = self._math.error
|
||||
|
||||
# 'default_randfunc' parameter:
|
||||
# None (default) - use Random.new().read
|
||||
# not None - use the specified function
|
||||
self._default_randfunc = kwargs.get('default_randfunc', None)
|
||||
self._current_randfunc = None
|
||||
|
||||
def _get_randfunc(self, randfunc):
|
||||
if randfunc is not None:
|
||||
return randfunc
|
||||
elif self._current_randfunc is None:
|
||||
self._current_randfunc = Random.new().read
|
||||
return self._current_randfunc
|
||||
|
||||
def generate(self, bits, randfunc=None, progress_func=None):
|
||||
"""Randomly generate a fresh, new DSA key.
|
||||
|
||||
:Parameters:
|
||||
bits : int
|
||||
Key length, or size (in bits) of the DSA modulus
|
||||
*p*.
|
||||
It must be a multiple of 64, in the closed
|
||||
interval [512,1024].
|
||||
randfunc : callable
|
||||
Random number generation function; it should accept
|
||||
a single integer N and return a string of random data
|
||||
N bytes long.
|
||||
If not specified, a new one will be instantiated
|
||||
from ``Crypto.Random``.
|
||||
progress_func : callable
|
||||
Optional function that will be called with a short string
|
||||
containing the key parameter currently being generated;
|
||||
it's useful for interactive applications where a user is
|
||||
waiting for a key to be generated.
|
||||
|
||||
:attention: You should always use a cryptographically secure random number generator,
|
||||
such as the one defined in the ``Crypto.Random`` module; **don't** just use the
|
||||
current time and the ``random`` module.
|
||||
|
||||
:Return: A DSA key object (`_DSAobj`).
|
||||
|
||||
:Raise ValueError:
|
||||
When **bits** is too little, too big, or not a multiple of 64.
|
||||
"""
|
||||
|
||||
# Check against FIPS 186-2, which says that the size of the prime p
|
||||
# must be a multiple of 64 bits between 512 and 1024
|
||||
for i in (0, 1, 2, 3, 4, 5, 6, 7, 8):
|
||||
if bits == 512 + 64*i:
|
||||
return self._generate(bits, randfunc, progress_func)
|
||||
|
||||
# The March 2006 draft of FIPS 186-3 also allows 2048 and 3072-bit
|
||||
# primes, but only with longer q values. Since the current DSA
|
||||
# implementation only supports a 160-bit q, we don't support larger
|
||||
# values.
|
||||
raise ValueError("Number of bits in p must be a multiple of 64 between 512 and 1024, not %d bits" % (bits,))
|
||||
|
||||
def _generate(self, bits, randfunc=None, progress_func=None):
|
||||
rf = self._get_randfunc(randfunc)
|
||||
obj = _DSA.generate_py(bits, rf, progress_func) # TODO: Don't use legacy _DSA module
|
||||
key = self._math.dsa_construct(obj.y, obj.g, obj.p, obj.q, obj.x)
|
||||
return _DSAobj(self, key)
|
||||
|
||||
def construct(self, tup):
|
||||
"""Construct a DSA key from a tuple of valid DSA components.
|
||||
|
||||
The modulus *p* must be a prime.
|
||||
|
||||
The following equations must apply:
|
||||
|
||||
- p-1 = 0 mod q
|
||||
- g^x = y mod p
|
||||
- 0 < x < q
|
||||
- 1 < g < p
|
||||
|
||||
:Parameters:
|
||||
tup : tuple
|
||||
A tuple of long integers, with 4 or 5 items
|
||||
in the following order:
|
||||
|
||||
1. Public key (*y*).
|
||||
2. Sub-group generator (*g*).
|
||||
3. Modulus, finite field order (*p*).
|
||||
4. Sub-group order (*q*).
|
||||
5. Private key (*x*). Optional.
|
||||
|
||||
:Return: A DSA key object (`_DSAobj`).
|
||||
"""
|
||||
key = self._math.dsa_construct(*tup)
|
||||
return _DSAobj(self, key)
|
||||
|
||||
def _importKeyDER(self, key_data, passphrase=None, params=None):
|
||||
"""Import a DSA key (public or private half), encoded in DER form."""
|
||||
|
||||
try:
|
||||
#
|
||||
# Dss-Parms ::= SEQUENCE {
|
||||
# p OCTET STRING,
|
||||
# q OCTET STRING,
|
||||
# g OCTET STRING
|
||||
# }
|
||||
#
|
||||
|
||||
# Try a simple private key first
|
||||
if params:
|
||||
x = decode_der(DerInteger, key_data).value
|
||||
params = decode_der(DerSequence, params) # Dss-Parms
|
||||
p, q, g = list(params)
|
||||
y = pow(g, x, p)
|
||||
tup = (y, g, p, q, x)
|
||||
return self.construct(tup)
|
||||
|
||||
der = decode_der(DerSequence, key_data)
|
||||
|
||||
# Try OpenSSL format for private keys
|
||||
if len(der) == 6 and der.hasOnlyInts() and der[0] == 0:
|
||||
tup = [der[comp] for comp in (4, 3, 1, 2, 5)]
|
||||
return self.construct(tup)
|
||||
|
||||
# Try SubjectPublicKeyInfo
|
||||
if len(der) == 2:
|
||||
try:
|
||||
algo = decode_der(DerSequence, der[0])
|
||||
algo_oid = decode_der(DerObjectId, algo[0]).value
|
||||
params = decode_der(DerSequence, algo[1]) # Dss-Parms
|
||||
|
||||
if algo_oid == oid and len(params) == 3 and\
|
||||
params.hasOnlyInts():
|
||||
bitmap = decode_der(DerBitString, der[1])
|
||||
pub_key = decode_der(DerInteger, bitmap.value)
|
||||
tup = [pub_key.value]
|
||||
tup += [params[comp] for comp in (2, 0, 1)]
|
||||
return self.construct(tup)
|
||||
except (ValueError, EOFError):
|
||||
pass
|
||||
|
||||
# Try unencrypted PKCS#8
|
||||
p8_pair = PKCS8.unwrap(key_data, passphrase)
|
||||
if p8_pair[0] == oid:
|
||||
return self._importKeyDER(p8_pair[1], passphrase, p8_pair[2])
|
||||
|
||||
except (ValueError, EOFError):
|
||||
pass
|
||||
|
||||
raise KeyFormatError("DSA key format is not supported")
|
||||
|
||||
def importKey(self, extern_key, passphrase=None):
|
||||
"""Import a DSA key (public or private).
|
||||
|
||||
:Parameters:
|
||||
extern_key : (byte) string
|
||||
The DSA key to import.
|
||||
|
||||
An DSA *public* key can be in any of the following formats:
|
||||
|
||||
- X.509 ``subjectPublicKeyInfo`` (binary or PEM)
|
||||
- OpenSSH (one line of text, see `RFC4253`_)
|
||||
|
||||
A DSA *private* key can be in any of the following formats:
|
||||
|
||||
- `PKCS#8`_ ``PrivateKeyInfo`` or ``EncryptedPrivateKeyInfo``
|
||||
DER SEQUENCE (binary or PEM encoding)
|
||||
- OpenSSL/OpenSSH (binary or PEM)
|
||||
|
||||
For details about the PEM encoding, see `RFC1421`_/`RFC1423`_.
|
||||
|
||||
The private key may be encrypted by means of a certain pass phrase
|
||||
either at the PEM level or at the PKCS#8 level.
|
||||
|
||||
passphrase : string
|
||||
In case of an encrypted private key, this is the pass phrase
|
||||
from which the decryption key is derived.
|
||||
|
||||
:Return: A DSA key object (`_DSAobj`).
|
||||
:Raise KeyFormatError:
|
||||
When the given key cannot be parsed (possibly because
|
||||
the pass phrase is wrong).
|
||||
|
||||
.. _RFC1421: http://www.ietf.org/rfc/rfc1421.txt
|
||||
.. _RFC1423: http://www.ietf.org/rfc/rfc1423.txt
|
||||
.. _RFC4253: http://www.ietf.org/rfc/rfc4253.txt
|
||||
.. _PKCS#8: http://www.ietf.org/rfc/rfc5208.txt
|
||||
"""
|
||||
|
||||
extern_key = tobytes(extern_key)
|
||||
if passphrase is not None:
|
||||
passphrase = tobytes(passphrase)
|
||||
|
||||
if extern_key.startswith(b('-----')):
|
||||
# This is probably a PEM encoded key
|
||||
(der, marker, enc_flag) = PEM.decode(tostr(extern_key), passphrase)
|
||||
if enc_flag:
|
||||
passphrase = None
|
||||
return self._importKeyDER(der, passphrase)
|
||||
|
||||
if extern_key.startswith(b('ssh-dss ')):
|
||||
# This is probably a public OpenSSH key
|
||||
keystring = binascii.a2b_base64(extern_key.split(b(' '))[1])
|
||||
keyparts = []
|
||||
while len(keystring) > 4:
|
||||
length = struct.unpack(">I", keystring[:4])[0]
|
||||
keyparts.append(keystring[4:4 + length])
|
||||
keystring = keystring[4 + length:]
|
||||
if keyparts[0] == b("ssh-dss"):
|
||||
tup = [bytes_to_long(keyparts[x]) for x in (4, 3, 1, 2)]
|
||||
return self.construct(tup)
|
||||
|
||||
if bord(extern_key[0]) == 0x30:
|
||||
# This is probably a DER encoded key
|
||||
return self._importKeyDER(extern_key, passphrase)
|
||||
|
||||
raise KeyFormatError("DSA key format is not supported")
|
||||
|
||||
#: `Object ID`_ for a DSA key.
|
||||
#:
|
||||
#: id-dsa ID ::= { iso(1) member-body(2) us(840) x9-57(10040) x9cm(4) 1 }
|
||||
#:
|
||||
#: .. _`Object ID`: http://www.alvestrand.no/objectid/1.2.840.10040.4.1.html
|
||||
oid = "1.2.840.10040.4.1"
|
||||
|
||||
_impl = DSAImplementation()
|
||||
generate = _impl.generate
|
||||
construct = _impl.construct
|
||||
importKey = _impl.importKey
|
||||
error = _impl.error
|
||||
|
||||
# vim:set ts=4 sw=4 sts=4 expandtab:
|
||||
|
382
modules/Crypto/PublicKey/ElGamal.py
Normal file
382
modules/Crypto/PublicKey/ElGamal.py
Normal file
@ -0,0 +1,382 @@
|
||||
#
|
||||
# ElGamal.py : ElGamal encryption/decryption and signatures
|
||||
#
|
||||
# Part of the Python Cryptography Toolkit
|
||||
#
|
||||
# Originally written by: A.M. Kuchling
|
||||
#
|
||||
# ===================================================================
|
||||
# The contents of this file are dedicated to the public domain. To
|
||||
# the extent that dedication to the public domain is not available,
|
||||
# everyone is granted a worldwide, perpetual, royalty-free,
|
||||
# non-exclusive license to exercise all rights associated with the
|
||||
# contents of this file for any purpose whatsoever.
|
||||
# No rights are reserved.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
# SOFTWARE.
|
||||
# ===================================================================
|
||||
|
||||
"""ElGamal public-key algorithm (randomized encryption and signature).
|
||||
|
||||
Signature algorithm
|
||||
-------------------
|
||||
The security of the ElGamal signature scheme is based (like DSA) on the discrete
|
||||
logarithm problem (DLP_). Given a cyclic group, a generator *g*,
|
||||
and an element *h*, it is hard to find an integer *x* such that *g^x = h*.
|
||||
|
||||
The group is the largest multiplicative sub-group of the integers modulo *p*,
|
||||
with *p* prime.
|
||||
The signer holds a value *x* (*0<x<p-1*) as private key, and its public
|
||||
key (*y* where *y=g^x mod p*) is distributed.
|
||||
|
||||
The ElGamal signature is twice as big as *p*.
|
||||
|
||||
Encryption algorithm
|
||||
--------------------
|
||||
The security of the ElGamal encryption scheme is based on the computational
|
||||
Diffie-Hellman problem (CDH_). Given a cyclic group, a generator *g*,
|
||||
and two integers *a* and *b*, it is difficult to find
|
||||
the element *g^{ab}* when only *g^a* and *g^b* are known, and not *a* and *b*.
|
||||
|
||||
As before, the group is the largest multiplicative sub-group of the integers
|
||||
modulo *p*, with *p* prime.
|
||||
The receiver holds a value *a* (*0<a<p-1*) as private key, and its public key
|
||||
(*b* where *b*=g^a*) is given to the sender.
|
||||
|
||||
The ElGamal ciphertext is twice as big as *p*.
|
||||
|
||||
Domain parameters
|
||||
-----------------
|
||||
For both signature and encryption schemes, the values *(p,g)* are called
|
||||
*domain parameters*.
|
||||
They are not sensitive but must be distributed to all parties (senders and
|
||||
receivers).
|
||||
Different signers can share the same domain parameters, as can
|
||||
different recipients of encrypted messages.
|
||||
|
||||
Security
|
||||
--------
|
||||
Both DLP and CDH problem are believed to be difficult, and they have been proved
|
||||
such (and therefore secure) for more than 30 years.
|
||||
|
||||
The cryptographic strength is linked to the magnitude of *p*.
|
||||
In 2012, a sufficient size for *p* is deemed to be 2048 bits.
|
||||
For more information, see the most recent ECRYPT_ report.
|
||||
|
||||
Even though ElGamal algorithms are in theory reasonably secure for new designs,
|
||||
in practice there are no real good reasons for using them.
|
||||
The signature is four times larger than the equivalent DSA, and the ciphertext
|
||||
is two times larger than the equivalent RSA.
|
||||
|
||||
Functionality
|
||||
-------------
|
||||
This module provides facilities for generating new ElGamal keys and for constructing
|
||||
them from known components. ElGamal keys allows you to perform basic signing,
|
||||
verification, encryption, and decryption.
|
||||
|
||||
>>> from Crypto import Random
|
||||
>>> from Crypto.Random import random
|
||||
>>> from Crypto.PublicKey import ElGamal
|
||||
>>> from Crypto.Util.number import GCD
|
||||
>>> from Crypto.Hash import SHA
|
||||
>>>
|
||||
>>> message = "Hello"
|
||||
>>> key = ElGamal.generate(1024, Random.new().read)
|
||||
>>> h = SHA.new(message).digest()
|
||||
>>> while 1:
|
||||
>>> k = random.StrongRandom().randint(1,key.p-1)
|
||||
>>> if GCD(k,key.p-1)==1: break
|
||||
>>> sig = key.sign(h,k)
|
||||
>>> ...
|
||||
>>> if key.verify(h,sig):
|
||||
>>> print "OK"
|
||||
>>> else:
|
||||
>>> print "Incorrect signature"
|
||||
|
||||
.. _DLP: http://www.cosic.esat.kuleuven.be/publications/talk-78.pdf
|
||||
.. _CDH: http://en.wikipedia.org/wiki/Computational_Diffie%E2%80%93Hellman_assumption
|
||||
.. _ECRYPT: http://www.ecrypt.eu.org/documents/D.SPA.17.pdf
|
||||
"""
|
||||
|
||||
__revision__ = "$Id$"
|
||||
|
||||
__all__ = ['generate', 'construct', 'error', 'ElGamalobj']
|
||||
|
||||
from Crypto.PublicKey.pubkey import *
|
||||
from Crypto.Util import number
|
||||
from Crypto import Random
|
||||
|
||||
class error (Exception):
|
||||
pass
|
||||
|
||||
# Generate an ElGamal key with N bits
|
||||
def generate(bits, randfunc, progress_func=None):
|
||||
"""Randomly generate a fresh, new ElGamal key.
|
||||
|
||||
The key will be safe for use for both encryption and signature
|
||||
(although it should be used for **only one** purpose).
|
||||
|
||||
:Parameters:
|
||||
bits : int
|
||||
Key length, or size (in bits) of the modulus *p*.
|
||||
Recommended value is 2048.
|
||||
randfunc : callable
|
||||
Random number generation function; it should accept
|
||||
a single integer N and return a string of random data
|
||||
N bytes long.
|
||||
progress_func : callable
|
||||
Optional function that will be called with a short string
|
||||
containing the key parameter currently being generated;
|
||||
it's useful for interactive applications where a user is
|
||||
waiting for a key to be generated.
|
||||
|
||||
:attention: You should always use a cryptographically secure random number generator,
|
||||
such as the one defined in the ``Crypto.Random`` module; **don't** just use the
|
||||
current time and the ``random`` module.
|
||||
|
||||
:Return: An ElGamal key object (`ElGamalobj`).
|
||||
"""
|
||||
obj=ElGamalobj()
|
||||
# Generate a safe prime p
|
||||
# See Algorithm 4.86 in Handbook of Applied Cryptography
|
||||
if progress_func:
|
||||
progress_func('p\n')
|
||||
while 1:
|
||||
q = bignum(getPrime(bits-1, randfunc))
|
||||
obj.p = 2*q+1
|
||||
if number.isPrime(obj.p, randfunc=randfunc):
|
||||
break
|
||||
# Generate generator g
|
||||
# See Algorithm 4.80 in Handbook of Applied Cryptography
|
||||
# Note that the order of the group is n=p-1=2q, where q is prime
|
||||
if progress_func:
|
||||
progress_func('g\n')
|
||||
while 1:
|
||||
# We must avoid g=2 because of Bleichenbacher's attack described
|
||||
# in "Generating ElGamal signatures without knowning the secret key",
|
||||
# 1996
|
||||
#
|
||||
obj.g = number.getRandomRange(3, obj.p, randfunc)
|
||||
safe = 1
|
||||
if pow(obj.g, 2, obj.p)==1:
|
||||
safe=0
|
||||
if safe and pow(obj.g, q, obj.p)==1:
|
||||
safe=0
|
||||
# Discard g if it divides p-1 because of the attack described
|
||||
# in Note 11.67 (iii) in HAC
|
||||
if safe and divmod(obj.p-1, obj.g)[1]==0:
|
||||
safe=0
|
||||
# g^{-1} must not divide p-1 because of Khadir's attack
|
||||
# described in "Conditions of the generator for forging ElGamal
|
||||
# signature", 2011
|
||||
ginv = number.inverse(obj.g, obj.p)
|
||||
if safe and divmod(obj.p-1, ginv)[1]==0:
|
||||
safe=0
|
||||
if safe:
|
||||
break
|
||||
# Generate private key x
|
||||
if progress_func:
|
||||
progress_func('x\n')
|
||||
obj.x=number.getRandomRange(2, obj.p-1, randfunc)
|
||||
# Generate public key y
|
||||
if progress_func:
|
||||
progress_func('y\n')
|
||||
obj.y = pow(obj.g, obj.x, obj.p)
|
||||
return obj
|
||||
|
||||
def construct(tup):
|
||||
"""Construct an ElGamal key from a tuple of valid ElGamal components.
|
||||
|
||||
The modulus *p* must be a prime.
|
||||
|
||||
The following conditions must apply:
|
||||
|
||||
- 1 < g < p-1
|
||||
- g^{p-1} = 1 mod p
|
||||
- 1 < x < p-1
|
||||
- g^x = y mod p
|
||||
|
||||
:Parameters:
|
||||
tup : tuple
|
||||
A tuple of long integers, with 3 or 4 items
|
||||
in the following order:
|
||||
|
||||
1. Modulus (*p*).
|
||||
2. Generator (*g*).
|
||||
3. Public key (*y*).
|
||||
4. Private key (*x*). Optional.
|
||||
|
||||
:Return: An ElGamal key object (`ElGamalobj`).
|
||||
"""
|
||||
|
||||
obj=ElGamalobj()
|
||||
if len(tup) not in [3,4]:
|
||||
raise ValueError('argument for construct() wrong length')
|
||||
for i in range(len(tup)):
|
||||
field = obj.keydata[i]
|
||||
setattr(obj, field, tup[i])
|
||||
return obj
|
||||
|
||||
class ElGamalobj(pubkey):
|
||||
"""Class defining an ElGamal key.
|
||||
|
||||
:undocumented: __getstate__, __setstate__, __repr__, __getattr__
|
||||
"""
|
||||
|
||||
#: Dictionary of ElGamal parameters.
|
||||
#:
|
||||
#: A public key will only have the following entries:
|
||||
#:
|
||||
#: - **y**, the public key.
|
||||
#: - **g**, the generator.
|
||||
#: - **p**, the modulus.
|
||||
#:
|
||||
#: A private key will also have:
|
||||
#:
|
||||
#: - **x**, the private key.
|
||||
keydata=['p', 'g', 'y', 'x']
|
||||
|
||||
def __init__(self, randfunc=None):
|
||||
if randfunc is None:
|
||||
randfunc = Random.new().read
|
||||
self._randfunc = randfunc
|
||||
|
||||
def encrypt(self, plaintext, K):
|
||||
"""Encrypt a piece of data with ElGamal.
|
||||
|
||||
:Parameter plaintext: The piece of data to encrypt with ElGamal.
|
||||
It must be numerically smaller than the module (*p*).
|
||||
:Type plaintext: byte string or long
|
||||
|
||||
:Parameter K: A secret number, chosen randomly in the closed
|
||||
range *[1,p-2]*.
|
||||
:Type K: long (recommended) or byte string (not recommended)
|
||||
|
||||
:Return: A tuple with two items. Each item is of the same type as the
|
||||
plaintext (string or long).
|
||||
|
||||
:attention: selection of *K* is crucial for security. Generating a
|
||||
random number larger than *p-1* and taking the modulus by *p-1* is
|
||||
**not** secure, since smaller values will occur more frequently.
|
||||
Generating a random number systematically smaller than *p-1*
|
||||
(e.g. *floor((p-1)/8)* random bytes) is also **not** secure.
|
||||
In general, it shall not be possible for an attacker to know
|
||||
the value of any bit of K.
|
||||
|
||||
:attention: The number *K* shall not be reused for any other
|
||||
operation and shall be discarded immediately.
|
||||
"""
|
||||
return pubkey.encrypt(self, plaintext, K)
|
||||
|
||||
def decrypt(self, ciphertext):
|
||||
"""Decrypt a piece of data with ElGamal.
|
||||
|
||||
:Parameter ciphertext: The piece of data to decrypt with ElGamal.
|
||||
:Type ciphertext: byte string, long or a 2-item tuple as returned
|
||||
by `encrypt`
|
||||
|
||||
:Return: A byte string if ciphertext was a byte string or a tuple
|
||||
of byte strings. A long otherwise.
|
||||
"""
|
||||
return pubkey.decrypt(self, ciphertext)
|
||||
|
||||
def sign(self, M, K):
|
||||
"""Sign a piece of data with ElGamal.
|
||||
|
||||
:Parameter M: The piece of data to sign with ElGamal. It may
|
||||
not be longer in bit size than *p-1*.
|
||||
:Type M: byte string or long
|
||||
|
||||
:Parameter K: A secret number, chosen randomly in the closed
|
||||
range *[1,p-2]* and such that *gcd(k,p-1)=1*.
|
||||
:Type K: long (recommended) or byte string (not recommended)
|
||||
|
||||
:attention: selection of *K* is crucial for security. Generating a
|
||||
random number larger than *p-1* and taking the modulus by *p-1* is
|
||||
**not** secure, since smaller values will occur more frequently.
|
||||
Generating a random number systematically smaller than *p-1*
|
||||
(e.g. *floor((p-1)/8)* random bytes) is also **not** secure.
|
||||
In general, it shall not be possible for an attacker to know
|
||||
the value of any bit of K.
|
||||
|
||||
:attention: The number *K* shall not be reused for any other
|
||||
operation and shall be discarded immediately.
|
||||
|
||||
:attention: M must be be a cryptographic hash, otherwise an
|
||||
attacker may mount an existential forgery attack.
|
||||
|
||||
:Return: A tuple with 2 longs.
|
||||
"""
|
||||
return pubkey.sign(self, M, K)
|
||||
|
||||
def verify(self, M, signature):
|
||||
"""Verify the validity of an ElGamal signature.
|
||||
|
||||
:Parameter M: The expected message.
|
||||
:Type M: byte string or long
|
||||
|
||||
:Parameter signature: The ElGamal signature to verify.
|
||||
:Type signature: A tuple with 2 longs as return by `sign`
|
||||
|
||||
:Return: True if the signature is correct, False otherwise.
|
||||
"""
|
||||
return pubkey.verify(self, M, signature)
|
||||
|
||||
def _encrypt(self, M, K):
|
||||
a=pow(self.g, K, self.p)
|
||||
b=( M*pow(self.y, K, self.p) ) % self.p
|
||||
return ( a,b )
|
||||
|
||||
def _decrypt(self, M):
|
||||
if (not hasattr(self, 'x')):
|
||||
raise TypeError('Private key not available in this object')
|
||||
r = number.getRandomRange(2, self.p-1, self._randfunc)
|
||||
a_blind = (M[0] * pow(self.g, r, self.p)) % self.p
|
||||
ax=pow(a_blind, self.x, self.p)
|
||||
plaintext_blind = (M[1] * inverse(ax, self.p ) ) % self.p
|
||||
plaintext = (plaintext_blind * pow(self.y, r, self.p)) % self.p
|
||||
return plaintext
|
||||
|
||||
def _sign(self, M, K):
|
||||
if (not hasattr(self, 'x')):
|
||||
raise TypeError('Private key not available in this object')
|
||||
p1=self.p-1
|
||||
if (GCD(K, p1)!=1):
|
||||
raise ValueError('Bad K value: GCD(K,p-1)!=1')
|
||||
a=pow(self.g, K, self.p)
|
||||
t=(M-self.x*a) % p1
|
||||
while t<0: t=t+p1
|
||||
b=(t*inverse(K, p1)) % p1
|
||||
return (a, b)
|
||||
|
||||
def _verify(self, M, sig):
|
||||
if sig[0]<1 or sig[0]>self.p-1:
|
||||
return 0
|
||||
v1=pow(self.y, sig[0], self.p)
|
||||
v1=(v1*pow(sig[0], sig[1], self.p)) % self.p
|
||||
v2=pow(self.g, M, self.p)
|
||||
if v1==v2:
|
||||
return 1
|
||||
return 0
|
||||
|
||||
def size(self):
|
||||
return number.size(self.p) - 1
|
||||
|
||||
def has_private(self):
|
||||
if hasattr(self, 'x'):
|
||||
return 1
|
||||
else:
|
||||
return 0
|
||||
|
||||
def publickey(self):
|
||||
return construct((self.p, self.g, self.y))
|
||||
|
||||
|
||||
object=ElGamalobj
|
732
modules/Crypto/PublicKey/RSA.py
Normal file
732
modules/Crypto/PublicKey/RSA.py
Normal file
@ -0,0 +1,732 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# PublicKey/RSA.py : RSA public key primitive
|
||||
#
|
||||
# Written in 2008 by Dwayne C. Litzenberger <dlitz@dlitz.net>
|
||||
#
|
||||
# ===================================================================
|
||||
# The contents of this file are dedicated to the public domain. To
|
||||
# the extent that dedication to the public domain is not available,
|
||||
# everyone is granted a worldwide, perpetual, royalty-free,
|
||||
# non-exclusive license to exercise all rights associated with the
|
||||
# contents of this file for any purpose whatsoever.
|
||||
# No rights are reserved.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
# SOFTWARE.
|
||||
# ===================================================================
|
||||
|
||||
"""RSA public-key cryptography algorithm (signature and encryption).
|
||||
|
||||
RSA_ is the most widespread and used public key algorithm. Its security is
|
||||
based on the difficulty of factoring large integers. The algorithm has
|
||||
withstood attacks for 30 years, and it is therefore considered reasonably
|
||||
secure for new designs.
|
||||
|
||||
The algorithm can be used for both confidentiality (encryption) and
|
||||
authentication (digital signature). It is worth noting that signing and
|
||||
decryption are significantly slower than verification and encryption.
|
||||
The cryptograhic strength is primarily linked to the length of the modulus *n*.
|
||||
In 2012, a sufficient length is deemed to be 2048 bits. For more information,
|
||||
see the most recent ECRYPT_ report.
|
||||
|
||||
Both RSA ciphertext and RSA signature are as big as the modulus *n* (256
|
||||
bytes if *n* is 2048 bit long).
|
||||
|
||||
This module provides facilities for generating fresh, new RSA keys, constructing
|
||||
them from known components, exporting them, and importing them.
|
||||
|
||||
>>> from Crypto.PublicKey import RSA
|
||||
>>>
|
||||
>>> key = RSA.generate(2048)
|
||||
>>> f = open('mykey.pem','w')
|
||||
>>> f.write(key.exportKey('PEM'))
|
||||
>>> f.close()
|
||||
...
|
||||
>>> f = open('mykey.pem','r')
|
||||
>>> key = RSA.importKey(f.read())
|
||||
|
||||
Even though you may choose to directly use the methods of an RSA key object
|
||||
to perform the primitive cryptographic operations (e.g. `_RSAobj.encrypt`),
|
||||
it is recommended to use one of the standardized schemes instead (like
|
||||
`Crypto.Cipher.PKCS1_v1_5` or `Crypto.Signature.PKCS1_v1_5`).
|
||||
|
||||
.. _RSA: http://en.wikipedia.org/wiki/RSA_%28algorithm%29
|
||||
.. _ECRYPT: http://www.ecrypt.eu.org/documents/D.SPA.17.pdf
|
||||
|
||||
:sort: generate,construct,importKey,error
|
||||
"""
|
||||
|
||||
__revision__ = "$Id$"
|
||||
|
||||
__all__ = ['generate', 'construct', 'error', 'importKey', 'RSAImplementation',
|
||||
'_RSAobj', 'oid' , 'algorithmIdentifier' ]
|
||||
|
||||
import sys
|
||||
if sys.version_info[0] == 2 and sys.version_info[1] == 1:
|
||||
from Crypto.Util.py21compat import *
|
||||
from Crypto.Util.py3compat import *
|
||||
|
||||
from Crypto.Util.number import getRandomRange, bytes_to_long, long_to_bytes
|
||||
|
||||
from Crypto.PublicKey import _RSA, _slowmath, pubkey
|
||||
from Crypto.IO import PKCS8, PEM
|
||||
from Crypto import Random
|
||||
|
||||
from Crypto.Util.asn1 import *
|
||||
|
||||
import binascii
|
||||
import struct
|
||||
|
||||
from Crypto.Util.number import inverse
|
||||
|
||||
try:
|
||||
from Crypto.PublicKey import _fastmath
|
||||
except ImportError:
|
||||
_fastmath = None
|
||||
|
||||
def decode_der(obj_class, binstr):
|
||||
"""Instantiate a DER object class, decode a DER binary string in it, and
|
||||
return the object."""
|
||||
der = obj_class()
|
||||
der.decode(binstr)
|
||||
return der
|
||||
|
||||
class _RSAobj(pubkey.pubkey):
|
||||
"""Class defining an actual RSA key.
|
||||
|
||||
:undocumented: __getstate__, __setstate__, __repr__, __getattr__
|
||||
"""
|
||||
#: Dictionary of RSA parameters.
|
||||
#:
|
||||
#: A public key will only have the following entries:
|
||||
#:
|
||||
#: - **n**, the modulus.
|
||||
#: - **e**, the public exponent.
|
||||
#:
|
||||
#: A private key will also have:
|
||||
#:
|
||||
#: - **d**, the private exponent.
|
||||
#: - **p**, the first factor of n.
|
||||
#: - **q**, the second factor of n.
|
||||
#: - **u**, the CRT coefficient (1/p) mod q.
|
||||
keydata = ['n', 'e', 'd', 'p', 'q', 'u']
|
||||
|
||||
def __init__(self, implementation, key, randfunc=None):
|
||||
self.implementation = implementation
|
||||
self.key = key
|
||||
if randfunc is None:
|
||||
randfunc = Random.new().read
|
||||
self._randfunc = randfunc
|
||||
|
||||
def __getattr__(self, attrname):
|
||||
if attrname in self.keydata:
|
||||
# For backward compatibility, allow the user to get (not set) the
|
||||
# RSA key parameters directly from this object.
|
||||
return getattr(self.key, attrname)
|
||||
else:
|
||||
raise AttributeError("%s object has no %r attribute" % (self.__class__.__name__, attrname,))
|
||||
|
||||
def encrypt(self, plaintext, K):
|
||||
"""Encrypt a piece of data with RSA.
|
||||
|
||||
:Parameter plaintext: The piece of data to encrypt with RSA. It may not
|
||||
be numerically larger than the RSA module (**n**).
|
||||
:Type plaintext: byte string or long
|
||||
|
||||
:Parameter K: A random parameter (*for compatibility only. This
|
||||
value will be ignored*)
|
||||
:Type K: byte string or long
|
||||
|
||||
:attention: this function performs the plain, primitive RSA encryption
|
||||
(*textbook*). In real applications, you always need to use proper
|
||||
cryptographic padding, and you should not directly encrypt data with
|
||||
this method. Failure to do so may lead to security vulnerabilities.
|
||||
It is recommended to use modules
|
||||
`Crypto.Cipher.PKCS1_OAEP` or `Crypto.Cipher.PKCS1_v1_5` instead.
|
||||
|
||||
:Return: A tuple with two items. The first item is the ciphertext
|
||||
of the same type as the plaintext (string or long). The second item
|
||||
is always None.
|
||||
"""
|
||||
return pubkey.pubkey.encrypt(self, plaintext, K)
|
||||
|
||||
def decrypt(self, ciphertext):
|
||||
"""Decrypt a piece of data with RSA.
|
||||
|
||||
Decryption always takes place with blinding.
|
||||
|
||||
:attention: this function performs the plain, primitive RSA decryption
|
||||
(*textbook*). In real applications, you always need to use proper
|
||||
cryptographic padding, and you should not directly decrypt data with
|
||||
this method. Failure to do so may lead to security vulnerabilities.
|
||||
It is recommended to use modules
|
||||
`Crypto.Cipher.PKCS1_OAEP` or `Crypto.Cipher.PKCS1_v1_5` instead.
|
||||
|
||||
:Parameter ciphertext: The piece of data to decrypt with RSA. It may
|
||||
not be numerically larger than the RSA module (**n**). If a tuple,
|
||||
the first item is the actual ciphertext; the second item is ignored.
|
||||
|
||||
:Type ciphertext: byte string, long or a 2-item tuple as returned by
|
||||
`encrypt`
|
||||
|
||||
:Return: A byte string if ciphertext was a byte string or a tuple
|
||||
of byte strings. A long otherwise.
|
||||
"""
|
||||
return pubkey.pubkey.decrypt(self, ciphertext)
|
||||
|
||||
def sign(self, M, K):
|
||||
"""Sign a piece of data with RSA.
|
||||
|
||||
Signing always takes place with blinding.
|
||||
|
||||
:attention: this function performs the plain, primitive RSA decryption
|
||||
(*textbook*). In real applications, you always need to use proper
|
||||
cryptographic padding, and you should not directly sign data with
|
||||
this method. Failure to do so may lead to security vulnerabilities.
|
||||
It is recommended to use modules
|
||||
`Crypto.Signature.PKCS1_PSS` or `Crypto.Signature.PKCS1_v1_5` instead.
|
||||
|
||||
:Parameter M: The piece of data to sign with RSA. It may
|
||||
not be numerically larger than the RSA module (**n**).
|
||||
:Type M: byte string or long
|
||||
|
||||
:Parameter K: A random parameter (*for compatibility only. This
|
||||
value will be ignored*)
|
||||
:Type K: byte string or long
|
||||
|
||||
:Return: A 2-item tuple. The first item is the actual signature (a
|
||||
long). The second item is always None.
|
||||
"""
|
||||
return pubkey.pubkey.sign(self, M, K)
|
||||
|
||||
def verify(self, M, signature):
|
||||
"""Verify the validity of an RSA signature.
|
||||
|
||||
:attention: this function performs the plain, primitive RSA encryption
|
||||
(*textbook*). In real applications, you always need to use proper
|
||||
cryptographic padding, and you should not directly verify data with
|
||||
this method. Failure to do so may lead to security vulnerabilities.
|
||||
It is recommended to use modules
|
||||
`Crypto.Signature.PKCS1_PSS` or `Crypto.Signature.PKCS1_v1_5` instead.
|
||||
|
||||
:Parameter M: The expected message.
|
||||
:Type M: byte string or long
|
||||
|
||||
:Parameter signature: The RSA signature to verify. The first item of
|
||||
the tuple is the actual signature (a long not larger than the modulus
|
||||
**n**), whereas the second item is always ignored.
|
||||
:Type signature: A 2-item tuple as return by `sign`
|
||||
|
||||
:Return: True if the signature is correct, False otherwise.
|
||||
"""
|
||||
return pubkey.pubkey.verify(self, M, signature)
|
||||
|
||||
def _encrypt(self, c, K):
|
||||
return (self.key._encrypt(c),)
|
||||
|
||||
def _decrypt(self, c):
|
||||
#(ciphertext,) = c
|
||||
(ciphertext,) = c[:1] # HACK - We should use the previous line
|
||||
# instead, but this is more compatible and we're
|
||||
# going to replace the Crypto.PublicKey API soon
|
||||
# anyway.
|
||||
|
||||
# Blinded RSA decryption (to prevent timing attacks):
|
||||
# Step 1: Generate random secret blinding factor r, such that 0 < r < n-1
|
||||
r = getRandomRange(1, self.key.n-1, randfunc=self._randfunc)
|
||||
# Step 2: Compute c' = c * r**e mod n
|
||||
cp = self.key._blind(ciphertext, r)
|
||||
# Step 3: Compute m' = c'**d mod n (ordinary RSA decryption)
|
||||
mp = self.key._decrypt(cp)
|
||||
# Step 4: Compute m = m**(r-1) mod n
|
||||
return self.key._unblind(mp, r)
|
||||
|
||||
def _blind(self, m, r):
|
||||
return self.key._blind(m, r)
|
||||
|
||||
def _unblind(self, m, r):
|
||||
return self.key._unblind(m, r)
|
||||
|
||||
def _sign(self, m, K=None):
|
||||
return (self.key._sign(m),)
|
||||
|
||||
def _verify(self, m, sig):
|
||||
#(s,) = sig
|
||||
(s,) = sig[:1] # HACK - We should use the previous line instead, but
|
||||
# this is more compatible and we're going to replace
|
||||
# the Crypto.PublicKey API soon anyway.
|
||||
return self.key._verify(m, s)
|
||||
|
||||
def has_private(self):
|
||||
return self.key.has_private()
|
||||
|
||||
def size(self):
|
||||
return self.key.size()
|
||||
|
||||
def can_blind(self):
|
||||
return True
|
||||
|
||||
def can_encrypt(self):
|
||||
return True
|
||||
|
||||
def can_sign(self):
|
||||
return True
|
||||
|
||||
def publickey(self):
|
||||
return self.implementation.construct((self.key.n, self.key.e))
|
||||
|
||||
def __getstate__(self):
|
||||
d = {}
|
||||
for k in self.keydata:
|
||||
try:
|
||||
d[k] = getattr(self.key, k)
|
||||
except AttributeError:
|
||||
pass
|
||||
return d
|
||||
|
||||
def __setstate__(self, d):
|
||||
if not hasattr(self, 'implementation'):
|
||||
self.implementation = RSAImplementation()
|
||||
if not hasattr(self, '_randfunc'):
|
||||
self._randfunc = Random.new().read
|
||||
t = []
|
||||
for k in self.keydata:
|
||||
if not d.has_key(k):
|
||||
break
|
||||
t.append(d[k])
|
||||
self.key = self.implementation._math.rsa_construct(*tuple(t))
|
||||
|
||||
def __repr__(self):
|
||||
attrs = []
|
||||
for k in self.keydata:
|
||||
if k == 'n':
|
||||
attrs.append("n(%d)" % (self.size()+1,))
|
||||
elif hasattr(self.key, k):
|
||||
attrs.append(k)
|
||||
if self.has_private():
|
||||
attrs.append("private")
|
||||
# PY3K: This is meant to be text, do not change to bytes (data)
|
||||
return "<%s @0x%x %s>" % (self.__class__.__name__, id(self), ",".join(attrs))
|
||||
|
||||
def exportKey(self, format='PEM', passphrase=None, pkcs=1, protection=None):
|
||||
"""Export this RSA key.
|
||||
|
||||
:Parameters:
|
||||
format : string
|
||||
The format to use for wrapping the key:
|
||||
|
||||
- *'DER'*. Binary encoding.
|
||||
- *'PEM'*. Textual encoding, done according to `RFC1421`_/`RFC1423`_.
|
||||
- *'OpenSSH'*. Textual encoding, done according to OpenSSH specification.
|
||||
Only suitable for public keys (not private keys).
|
||||
|
||||
passphrase : string
|
||||
For private keys only. The pass phrase used for deriving the encryption
|
||||
key.
|
||||
|
||||
pkcs : integer
|
||||
For *DER* and *PEM* format only.
|
||||
The PKCS standard to follow for assembling the components of the key.
|
||||
You have two choices:
|
||||
|
||||
- **1** (default): the public key is embedded into
|
||||
an X.509 ``SubjectPublicKeyInfo`` DER SEQUENCE.
|
||||
The private key is embedded into a `PKCS#1`_
|
||||
``RSAPrivateKey`` DER SEQUENCE.
|
||||
- **8**: the private key is embedded into a `PKCS#8`_
|
||||
``PrivateKeyInfo`` DER SEQUENCE. This value cannot be used
|
||||
for public keys.
|
||||
|
||||
protection : string
|
||||
The encryption scheme to use for protecting the private key.
|
||||
|
||||
If ``None`` (default), the behavior depends on ``format``:
|
||||
|
||||
- For *DER*, the *PBKDF2WithHMAC-SHA1AndDES-EDE3-CBC*
|
||||
scheme is used. The following operations are performed:
|
||||
|
||||
1. A 16 byte Triple DES key is derived from the passphrase
|
||||
using `Crypto.Protocol.KDF.PBKDF2` with 8 bytes salt,
|
||||
and 1 000 iterations of `Crypto.Hash.HMAC`.
|
||||
2. The private key is encrypted using CBC.
|
||||
3. The encrypted key is encoded according to PKCS#8.
|
||||
|
||||
- For *PEM*, the obsolete PEM encryption scheme is used.
|
||||
It is based on MD5 for key derivation, and Triple DES for encryption.
|
||||
|
||||
Specifying a value for ``protection`` is only meaningful for PKCS#8
|
||||
(that is, ``pkcs=8``) and only if a pass phrase is present too.
|
||||
|
||||
The supported schemes for PKCS#8 are listed in the
|
||||
`Crypto.IO.PKCS8` module (see ``wrap_algo`` parameter).
|
||||
|
||||
:Return: A byte string with the encoded public or private half
|
||||
of the key.
|
||||
:Raise ValueError:
|
||||
When the format is unknown or when you try to encrypt a private
|
||||
key with *DER* format and PKCS#1.
|
||||
:attention:
|
||||
If you don't provide a pass phrase, the private key will be
|
||||
exported in the clear!
|
||||
|
||||
.. _RFC1421: http://www.ietf.org/rfc/rfc1421.txt
|
||||
.. _RFC1423: http://www.ietf.org/rfc/rfc1423.txt
|
||||
.. _`PKCS#1`: http://www.ietf.org/rfc/rfc3447.txt
|
||||
.. _`PKCS#8`: http://www.ietf.org/rfc/rfc5208.txt
|
||||
"""
|
||||
if passphrase is not None:
|
||||
passphrase = tobytes(passphrase)
|
||||
if format=='OpenSSH':
|
||||
eb = long_to_bytes(self.e)
|
||||
nb = long_to_bytes(self.n)
|
||||
if bord(eb[0]) & 0x80: eb=bchr(0x00)+eb
|
||||
if bord(nb[0]) & 0x80: nb=bchr(0x00)+nb
|
||||
keyparts = [ b('ssh-rsa'), eb, nb ]
|
||||
keystring = b('').join([ struct.pack(">I",len(kp))+kp for kp in keyparts])
|
||||
return b('ssh-rsa ')+binascii.b2a_base64(keystring)[:-1]
|
||||
|
||||
# DER format is always used, even in case of PEM, which simply
|
||||
# encodes it into BASE64.
|
||||
if self.has_private():
|
||||
binary_key = newDerSequence(
|
||||
0,
|
||||
self.n,
|
||||
self.e,
|
||||
self.d,
|
||||
self.p,
|
||||
self.q,
|
||||
self.d % (self.p-1),
|
||||
self.d % (self.q-1),
|
||||
inverse(self.q, self.p)
|
||||
).encode()
|
||||
if pkcs==1:
|
||||
keyType = 'RSA PRIVATE'
|
||||
if format=='DER' and passphrase:
|
||||
raise ValueError("PKCS#1 private key cannot be encrypted")
|
||||
else: # PKCS#8
|
||||
if format=='PEM' and protection is None:
|
||||
keyType = 'PRIVATE'
|
||||
binary_key = PKCS8.wrap(binary_key, oid, None)
|
||||
else:
|
||||
keyType = 'ENCRYPTED PRIVATE'
|
||||
if not protection:
|
||||
protection = 'PBKDF2WithHMAC-SHA1AndDES-EDE3-CBC'
|
||||
binary_key = PKCS8.wrap(binary_key, oid, passphrase, protection)
|
||||
passphrase = None
|
||||
else:
|
||||
keyType = "RSA PUBLIC"
|
||||
binary_key = newDerSequence(
|
||||
algorithmIdentifier,
|
||||
newDerBitString(
|
||||
newDerSequence( self.n, self.e )
|
||||
)
|
||||
).encode()
|
||||
if format=='DER':
|
||||
return binary_key
|
||||
if format=='PEM':
|
||||
pem_str = PEM.encode(binary_key, keyType+" KEY", passphrase, self._randfunc)
|
||||
return tobytes(pem_str)
|
||||
raise ValueError("Unknown key format '%s'. Cannot export the RSA key." % format)
|
||||
|
||||
class RSAImplementation(object):
|
||||
"""
|
||||
An RSA key factory.
|
||||
|
||||
This class is only internally used to implement the methods of the `Crypto.PublicKey.RSA` module.
|
||||
|
||||
:sort: __init__,generate,construct,importKey
|
||||
:undocumented: _g*, _i*
|
||||
"""
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
"""Create a new RSA key factory.
|
||||
|
||||
:Keywords:
|
||||
use_fast_math : bool
|
||||
Specify which mathematic library to use:
|
||||
|
||||
- *None* (default). Use fastest math available.
|
||||
- *True* . Use fast math.
|
||||
- *False* . Use slow math.
|
||||
default_randfunc : callable
|
||||
Specify how to collect random data:
|
||||
|
||||
- *None* (default). Use Random.new().read().
|
||||
- not *None* . Use the specified function directly.
|
||||
:Raise RuntimeError:
|
||||
When **use_fast_math** =True but fast math is not available.
|
||||
"""
|
||||
use_fast_math = kwargs.get('use_fast_math', None)
|
||||
if use_fast_math is None: # Automatic
|
||||
if _fastmath is not None:
|
||||
self._math = _fastmath
|
||||
else:
|
||||
self._math = _slowmath
|
||||
|
||||
elif use_fast_math: # Explicitly select fast math
|
||||
if _fastmath is not None:
|
||||
self._math = _fastmath
|
||||
else:
|
||||
raise RuntimeError("fast math module not available")
|
||||
|
||||
else: # Explicitly select slow math
|
||||
self._math = _slowmath
|
||||
|
||||
self.error = self._math.error
|
||||
|
||||
self._default_randfunc = kwargs.get('default_randfunc', None)
|
||||
self._current_randfunc = None
|
||||
|
||||
def _get_randfunc(self, randfunc):
|
||||
if randfunc is not None:
|
||||
return randfunc
|
||||
elif self._current_randfunc is None:
|
||||
self._current_randfunc = Random.new().read
|
||||
return self._current_randfunc
|
||||
|
||||
def generate(self, bits, randfunc=None, progress_func=None, e=65537):
|
||||
"""Randomly generate a fresh, new RSA key.
|
||||
|
||||
:Parameters:
|
||||
bits : int
|
||||
Key length, or size (in bits) of the RSA modulus.
|
||||
It must be a multiple of 256, and no smaller than 1024.
|
||||
|
||||
randfunc : callable
|
||||
Random number generation function; it should accept
|
||||
a single integer N and return a string of random data
|
||||
N bytes long.
|
||||
If not specified, a new one will be instantiated
|
||||
from ``Crypto.Random``.
|
||||
|
||||
progress_func : callable
|
||||
Optional function that will be called with a short string
|
||||
containing the key parameter currently being generated;
|
||||
it's useful for interactive applications where a user is
|
||||
waiting for a key to be generated.
|
||||
|
||||
e : int
|
||||
Public RSA exponent. It must be an odd positive integer.
|
||||
It is typically a small number with very few ones in its
|
||||
binary representation.
|
||||
The default value 65537 (= ``0b10000000000000001`` ) is a safe
|
||||
choice: other common values are 5, 7, 17, and 257.
|
||||
|
||||
:attention: You should always use a cryptographically secure random number generator,
|
||||
such as the one defined in the ``Crypto.Random`` module; **don't** just use the
|
||||
current time and the ``random`` module.
|
||||
|
||||
:attention: Exponent 3 is also widely used, but it requires very special care when padding
|
||||
the message.
|
||||
|
||||
:Return: An RSA key object (`_RSAobj`).
|
||||
|
||||
:Raise ValueError:
|
||||
When **bits** is too little or not a multiple of 256, or when
|
||||
**e** is not odd or smaller than 2.
|
||||
"""
|
||||
if bits < 1024 or (bits & 0xff) != 0:
|
||||
# pubkey.getStrongPrime doesn't like anything that's not a multiple of 256 and >= 1024
|
||||
raise ValueError("RSA modulus length must be a multiple of 256 and >= 1024")
|
||||
if e%2==0 or e<3:
|
||||
raise ValueError("RSA public exponent must be a positive, odd integer larger than 2.")
|
||||
rf = self._get_randfunc(randfunc)
|
||||
obj = _RSA.generate_py(bits, rf, progress_func, e) # TODO: Don't use legacy _RSA module
|
||||
key = self._math.rsa_construct(obj.n, obj.e, obj.d, obj.p, obj.q, obj.u)
|
||||
return _RSAobj(self, key)
|
||||
|
||||
def construct(self, tup):
|
||||
"""Construct an RSA key from a tuple of valid RSA components.
|
||||
|
||||
The modulus **n** must be the product of two primes.
|
||||
The public exponent **e** must be odd and larger than 1.
|
||||
|
||||
In case of a private key, the following equations must apply:
|
||||
|
||||
- e != 1
|
||||
- p*q = n
|
||||
- e*d = 1 mod (p-1)(q-1)
|
||||
- p*u = 1 mod q
|
||||
|
||||
:Parameters:
|
||||
tup : tuple
|
||||
A tuple of long integers, with at least 2 and no
|
||||
more than 6 items. The items come in the following order:
|
||||
|
||||
1. RSA modulus (n).
|
||||
2. Public exponent (e).
|
||||
3. Private exponent (d). Only required if the key is private.
|
||||
4. First factor of n (p). Optional.
|
||||
5. Second factor of n (q). Optional.
|
||||
6. CRT coefficient, (1/p) mod q (u). Optional.
|
||||
|
||||
:Return: An RSA key object (`_RSAobj`).
|
||||
"""
|
||||
key = self._math.rsa_construct(*tup)
|
||||
return _RSAobj(self, key)
|
||||
|
||||
def _importKeyDER(self, extern_key, passphrase=None):
|
||||
"""Import an RSA key (public or private half), encoded in DER form."""
|
||||
|
||||
try:
|
||||
|
||||
der = decode_der(DerSequence, extern_key)
|
||||
|
||||
# Try PKCS#1 first, for a private key
|
||||
if len(der) == 9 and der.hasOnlyInts() and der[0] == 0:
|
||||
# ASN.1 RSAPrivateKey element
|
||||
del der[6:] # Remove d mod (p-1),
|
||||
# d mod (q-1), and
|
||||
# q^{-1} mod p
|
||||
der.append(inverse(der[4], der[5])) # Add p^{-1} mod q
|
||||
del der[0] # Remove version
|
||||
return self.construct(der[:])
|
||||
|
||||
# Keep on trying PKCS#1, but now for a public key
|
||||
if len(der) == 2:
|
||||
try:
|
||||
# The DER object is an RSAPublicKey SEQUENCE with
|
||||
# two elements
|
||||
if der.hasOnlyInts():
|
||||
return self.construct(der[:])
|
||||
# The DER object is a SubjectPublicKeyInfo SEQUENCE
|
||||
# with two elements: an 'algorithmIdentifier' and a
|
||||
# 'subjectPublicKey'BIT STRING.
|
||||
# 'algorithmIdentifier' takes the value given at the
|
||||
# module level.
|
||||
# 'subjectPublicKey' encapsulates the actual ASN.1
|
||||
# RSAPublicKey element.
|
||||
if der[0] == algorithmIdentifier:
|
||||
bitmap = decode_der(DerBitString, der[1])
|
||||
rsaPub = decode_der(DerSequence, bitmap.value)
|
||||
if len(rsaPub) == 2 and rsaPub.hasOnlyInts():
|
||||
return self.construct(rsaPub[:])
|
||||
except (ValueError, EOFError):
|
||||
pass
|
||||
|
||||
# Try PKCS#8 (possibly encrypted)
|
||||
k = PKCS8.unwrap(extern_key, passphrase)
|
||||
if k[0] == oid:
|
||||
return self._importKeyDER(k[1], passphrase)
|
||||
|
||||
except (ValueError, EOFError):
|
||||
pass
|
||||
|
||||
raise ValueError("RSA key format is not supported")
|
||||
|
||||
def importKey(self, extern_key, passphrase=None):
|
||||
"""Import an RSA key (public or private half), encoded in standard
|
||||
form.
|
||||
|
||||
:Parameter extern_key:
|
||||
The RSA key to import, encoded as a string.
|
||||
|
||||
An RSA public key can be in any of the following formats:
|
||||
|
||||
- X.509 ``subjectPublicKeyInfo`` DER SEQUENCE (binary or PEM
|
||||
encoding)
|
||||
- `PKCS#1`_ ``RSAPublicKey`` DER SEQUENCE (binary or PEM encoding)
|
||||
- OpenSSH (textual public key only)
|
||||
|
||||
An RSA private key can be in any of the following formats:
|
||||
|
||||
- PKCS#1 ``RSAPrivateKey`` DER SEQUENCE (binary or PEM encoding)
|
||||
- `PKCS#8`_ ``PrivateKeyInfo`` or ``EncryptedPrivateKeyInfo``
|
||||
DER SEQUENCE (binary or PEM encoding)
|
||||
- OpenSSH (textual public key only)
|
||||
|
||||
For details about the PEM encoding, see `RFC1421`_/`RFC1423`_.
|
||||
|
||||
The private key may be encrypted by means of a certain pass phrase
|
||||
either at the PEM level or at the PKCS#8 level.
|
||||
:Type extern_key: string
|
||||
|
||||
:Parameter passphrase:
|
||||
In case of an encrypted private key, this is the pass phrase from
|
||||
which the decryption key is derived.
|
||||
:Type passphrase: string
|
||||
|
||||
:Return: An RSA key object (`_RSAobj`).
|
||||
|
||||
:Raise ValueError/IndexError/TypeError:
|
||||
When the given key cannot be parsed (possibly because the pass
|
||||
phrase is wrong).
|
||||
|
||||
.. _RFC1421: http://www.ietf.org/rfc/rfc1421.txt
|
||||
.. _RFC1423: http://www.ietf.org/rfc/rfc1423.txt
|
||||
.. _`PKCS#1`: http://www.ietf.org/rfc/rfc3447.txt
|
||||
.. _`PKCS#8`: http://www.ietf.org/rfc/rfc5208.txt
|
||||
"""
|
||||
extern_key = tobytes(extern_key)
|
||||
if passphrase is not None:
|
||||
passphrase = tobytes(passphrase)
|
||||
|
||||
if extern_key.startswith(b('-----')):
|
||||
# This is probably a PEM encoded key.
|
||||
(der, marker, enc_flag) = PEM.decode(tostr(extern_key), passphrase)
|
||||
if enc_flag:
|
||||
passphrase = None
|
||||
return self._importKeyDER(der, passphrase)
|
||||
|
||||
if extern_key.startswith(b('ssh-rsa ')):
|
||||
# This is probably an OpenSSH key
|
||||
keystring = binascii.a2b_base64(extern_key.split(b(' '))[1])
|
||||
keyparts = []
|
||||
while len(keystring) > 4:
|
||||
l = struct.unpack(">I", keystring[:4])[0]
|
||||
keyparts.append(keystring[4:4 + l])
|
||||
keystring = keystring[4 + l:]
|
||||
e = bytes_to_long(keyparts[1])
|
||||
n = bytes_to_long(keyparts[2])
|
||||
return self.construct([n, e])
|
||||
|
||||
if bord(extern_key[0]) == 0x30:
|
||||
# This is probably a DER encoded key
|
||||
return self._importKeyDER(extern_key, passphrase)
|
||||
|
||||
raise ValueError("RSA key format is not supported")
|
||||
|
||||
#: `Object ID`_ for the RSA encryption algorithm. This OID often indicates
|
||||
#: a generic RSA key, even when such key will be actually used for digital
|
||||
#: signatures.
|
||||
#:
|
||||
#: .. _`Object ID`: http://www.alvestrand.no/objectid/1.2.840.113549.1.1.1.html
|
||||
oid = "1.2.840.113549.1.1.1"
|
||||
|
||||
#: This is the standard DER object that qualifies a cryptographic algorithm
|
||||
#: in ASN.1-based data structures (e.g. X.509 certificates).
|
||||
algorithmIdentifier = DerSequence(
|
||||
[DerObjectId(oid).encode(), # algorithm field
|
||||
DerNull().encode()] # parameters field
|
||||
).encode()
|
||||
|
||||
_impl = RSAImplementation()
|
||||
#:
|
||||
#: Randomly generate a fresh, new RSA key object.
|
||||
#:
|
||||
#: See `RSAImplementation.generate`.
|
||||
#:
|
||||
generate = _impl.generate
|
||||
#:
|
||||
#: Construct an RSA key object from a tuple of valid RSA components.
|
||||
#:
|
||||
#: See `RSAImplementation.construct`.
|
||||
#:
|
||||
construct = _impl.construct
|
||||
#:
|
||||
#: Import an RSA key (public or private half), encoded in standard form.
|
||||
#:
|
||||
#: See `RSAImplementation.importKey`.
|
||||
#:
|
||||
importKey = _impl.importKey
|
||||
error = _impl.error
|
||||
|
||||
# vim:set ts=4 sw=4 sts=4 expandtab:
|
||||
|
115
modules/Crypto/PublicKey/_DSA.py
Normal file
115
modules/Crypto/PublicKey/_DSA.py
Normal file
@ -0,0 +1,115 @@
|
||||
|
||||
#
|
||||
# DSA.py : Digital Signature Algorithm
|
||||
#
|
||||
# Part of the Python Cryptography Toolkit
|
||||
#
|
||||
# Written by Andrew Kuchling, Paul Swartz, and others
|
||||
#
|
||||
# ===================================================================
|
||||
# The contents of this file are dedicated to the public domain. To
|
||||
# the extent that dedication to the public domain is not available,
|
||||
# everyone is granted a worldwide, perpetual, royalty-free,
|
||||
# non-exclusive license to exercise all rights associated with the
|
||||
# contents of this file for any purpose whatsoever.
|
||||
# No rights are reserved.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
# SOFTWARE.
|
||||
# ===================================================================
|
||||
#
|
||||
|
||||
__revision__ = "$Id$"
|
||||
|
||||
from Crypto.PublicKey.pubkey import *
|
||||
from Crypto.Util import number
|
||||
from Crypto.Util.number import bytes_to_long, long_to_bytes
|
||||
from Crypto.Hash import SHA1
|
||||
from Crypto.Util.py3compat import *
|
||||
|
||||
class error (Exception):
|
||||
pass
|
||||
|
||||
def generateQ(randfunc):
|
||||
S=randfunc(20)
|
||||
hash1=SHA1.new(S).digest()
|
||||
hash2=SHA1.new(long_to_bytes(bytes_to_long(S)+1)).digest()
|
||||
q = bignum(0)
|
||||
for i in range(0,20):
|
||||
c=bord(hash1[i])^bord(hash2[i])
|
||||
if i==0:
|
||||
c=c | 128
|
||||
if i==19:
|
||||
c= c | 1
|
||||
q=q*256+c
|
||||
while (not isPrime(q)):
|
||||
q=q+2
|
||||
if pow(2,159L) < q < pow(2,160L):
|
||||
return S, q
|
||||
raise RuntimeError('Bad q value generated')
|
||||
|
||||
def generate_py(bits, randfunc, progress_func=None):
|
||||
"""generate(bits:int, randfunc:callable, progress_func:callable)
|
||||
|
||||
Generate a DSA key of length 'bits', using 'randfunc' to get
|
||||
random data and 'progress_func', if present, to display
|
||||
the progress of the key generation.
|
||||
"""
|
||||
|
||||
if bits<160:
|
||||
raise ValueError('Key length < 160 bits')
|
||||
obj=DSAobj()
|
||||
# Generate string S and prime q
|
||||
if progress_func:
|
||||
progress_func('p,q\n')
|
||||
while (1):
|
||||
S, obj.q = generateQ(randfunc)
|
||||
n=divmod(bits-1, 160)[0]
|
||||
C, N, V = 0, 2, {}
|
||||
b=(obj.q >> 5) & 15
|
||||
powb=pow(bignum(2), b)
|
||||
powL1=pow(bignum(2), bits-1)
|
||||
while C<4096:
|
||||
for k in range(0, n+1):
|
||||
V[k]=bytes_to_long(SHA1.new(S+bstr(N)+bstr(k)).digest())
|
||||
W=V[n] % powb
|
||||
for k in range(n-1, -1, -1):
|
||||
W=(W<<160L)+V[k]
|
||||
X=W+powL1
|
||||
p=X-(X%(2*obj.q)-1)
|
||||
if powL1<=p and isPrime(p):
|
||||
break
|
||||
C, N = C+1, N+n+1
|
||||
if C<4096:
|
||||
break
|
||||
if progress_func:
|
||||
progress_func('4096 multiples failed\n')
|
||||
|
||||
obj.p = p
|
||||
power=divmod(p-1, obj.q)[0]
|
||||
if progress_func:
|
||||
progress_func('h,g\n')
|
||||
while (1):
|
||||
h=bytes_to_long(randfunc(bits)) % (p-1)
|
||||
g=pow(h, power, p)
|
||||
if 1<h<p-1 and g>1:
|
||||
break
|
||||
obj.g=g
|
||||
if progress_func:
|
||||
progress_func('x,y\n')
|
||||
while (1):
|
||||
x=bytes_to_long(randfunc(20))
|
||||
if 0 < x < obj.q:
|
||||
break
|
||||
obj.x, obj.y = x, pow(g, x, p)
|
||||
return obj
|
||||
|
||||
class DSAobj:
|
||||
pass
|
||||
|
81
modules/Crypto/PublicKey/_RSA.py
Normal file
81
modules/Crypto/PublicKey/_RSA.py
Normal file
@ -0,0 +1,81 @@
|
||||
#
|
||||
# RSA.py : RSA encryption/decryption
|
||||
#
|
||||
# Part of the Python Cryptography Toolkit
|
||||
#
|
||||
# Written by Andrew Kuchling, Paul Swartz, and others
|
||||
#
|
||||
# ===================================================================
|
||||
# The contents of this file are dedicated to the public domain. To
|
||||
# the extent that dedication to the public domain is not available,
|
||||
# everyone is granted a worldwide, perpetual, royalty-free,
|
||||
# non-exclusive license to exercise all rights associated with the
|
||||
# contents of this file for any purpose whatsoever.
|
||||
# No rights are reserved.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
# SOFTWARE.
|
||||
# ===================================================================
|
||||
#
|
||||
|
||||
__revision__ = "$Id$"
|
||||
|
||||
from Crypto.PublicKey import pubkey
|
||||
from Crypto.Util import number
|
||||
|
||||
def generate_py(bits, randfunc, progress_func=None, e=65537):
|
||||
"""generate(bits:int, randfunc:callable, progress_func:callable, e:int)
|
||||
|
||||
Generate an RSA key of length 'bits', public exponent 'e'(which must be
|
||||
odd), using 'randfunc' to get random data and 'progress_func',
|
||||
if present, to display the progress of the key generation.
|
||||
"""
|
||||
obj=RSAobj()
|
||||
obj.e = long(e)
|
||||
|
||||
# Generate the prime factors of n
|
||||
if progress_func:
|
||||
progress_func('p,q\n')
|
||||
p = q = 1L
|
||||
while number.size(p*q) < bits:
|
||||
# Note that q might be one bit longer than p if somebody specifies an odd
|
||||
# number of bits for the key. (Why would anyone do that? You don't get
|
||||
# more security.)
|
||||
p = pubkey.getStrongPrime(bits>>1, obj.e, 1e-12, randfunc)
|
||||
q = pubkey.getStrongPrime(bits - (bits>>1), obj.e, 1e-12, randfunc)
|
||||
|
||||
# It's OK for p to be larger than q, but let's be
|
||||
# kind to the function that will invert it for
|
||||
# th calculation of u.
|
||||
if p > q:
|
||||
(p, q)=(q, p)
|
||||
obj.p = p
|
||||
obj.q = q
|
||||
|
||||
if progress_func:
|
||||
progress_func('u\n')
|
||||
obj.u = pubkey.inverse(obj.p, obj.q)
|
||||
obj.n = obj.p*obj.q
|
||||
|
||||
if progress_func:
|
||||
progress_func('d\n')
|
||||
obj.d=pubkey.inverse(obj.e, (obj.p-1)*(obj.q-1))
|
||||
|
||||
assert bits <= 1+obj.size(), "Generated key is too small"
|
||||
|
||||
return obj
|
||||
|
||||
class RSAobj(pubkey.pubkey):
|
||||
|
||||
def size(self):
|
||||
"""size() : int
|
||||
Return the maximum number of bits that can be handled by this key.
|
||||
"""
|
||||
return number.size(self.n) - 1
|
||||
|
44
modules/Crypto/PublicKey/__init__.py
Normal file
44
modules/Crypto/PublicKey/__init__.py
Normal file
@ -0,0 +1,44 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# ===================================================================
|
||||
# The contents of this file are dedicated to the public domain. To
|
||||
# the extent that dedication to the public domain is not available,
|
||||
# everyone is granted a worldwide, perpetual, royalty-free,
|
||||
# non-exclusive license to exercise all rights associated with the
|
||||
# contents of this file for any purpose whatsoever.
|
||||
# No rights are reserved.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
# SOFTWARE.
|
||||
# ===================================================================
|
||||
|
||||
"""Public-key encryption and signature algorithms.
|
||||
|
||||
Public-key encryption uses two different keys, one for encryption and
|
||||
one for decryption. The encryption key can be made public, and the
|
||||
decryption key is kept private. Many public-key algorithms can also
|
||||
be used to sign messages, and some can *only* be used for signatures.
|
||||
|
||||
======================== =============================================
|
||||
Module Description
|
||||
======================== =============================================
|
||||
Crypto.PublicKey.DSA Digital Signature Algorithm (Signature only)
|
||||
Crypto.PublicKey.ElGamal (Signing and encryption)
|
||||
Crypto.PublicKey.RSA (Signing, encryption, and blinding)
|
||||
======================== =============================================
|
||||
|
||||
:undocumented: _DSA, _RSA, _fastmath, _slowmath, pubkey
|
||||
"""
|
||||
|
||||
class KeyFormatError(ValueError):
|
||||
pass
|
||||
|
||||
__all__ = ['RSA', 'DSA', 'ElGamal']
|
||||
__revision__ = "$Id$"
|
||||
|
187
modules/Crypto/PublicKey/_slowmath.py
Normal file
187
modules/Crypto/PublicKey/_slowmath.py
Normal file
@ -0,0 +1,187 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# PubKey/RSA/_slowmath.py : Pure Python implementation of the RSA portions of _fastmath
|
||||
#
|
||||
# Written in 2008 by Dwayne C. Litzenberger <dlitz@dlitz.net>
|
||||
#
|
||||
# ===================================================================
|
||||
# The contents of this file are dedicated to the public domain. To
|
||||
# the extent that dedication to the public domain is not available,
|
||||
# everyone is granted a worldwide, perpetual, royalty-free,
|
||||
# non-exclusive license to exercise all rights associated with the
|
||||
# contents of this file for any purpose whatsoever.
|
||||
# No rights are reserved.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
# SOFTWARE.
|
||||
# ===================================================================
|
||||
|
||||
"""Pure Python implementation of the RSA-related portions of Crypto.PublicKey._fastmath."""
|
||||
|
||||
__revision__ = "$Id$"
|
||||
|
||||
__all__ = ['rsa_construct']
|
||||
|
||||
import sys
|
||||
|
||||
if sys.version_info[0] == 2 and sys.version_info[1] == 1:
|
||||
from Crypto.Util.py21compat import *
|
||||
from Crypto.Util.number import size, inverse, GCD
|
||||
|
||||
class error(Exception):
|
||||
pass
|
||||
|
||||
class _RSAKey(object):
|
||||
def _blind(self, m, r):
|
||||
# compute r**e * m (mod n)
|
||||
return m * pow(r, self.e, self.n)
|
||||
|
||||
def _unblind(self, m, r):
|
||||
# compute m / r (mod n)
|
||||
return inverse(r, self.n) * m % self.n
|
||||
|
||||
def _decrypt(self, c):
|
||||
# compute c**d (mod n)
|
||||
if not self.has_private():
|
||||
raise TypeError("No private key")
|
||||
if (hasattr(self,'p') and hasattr(self,'q') and hasattr(self,'u')):
|
||||
m1 = pow(c, self.d % (self.p-1), self.p)
|
||||
m2 = pow(c, self.d % (self.q-1), self.q)
|
||||
h = m2 - m1
|
||||
if (h<0):
|
||||
h = h + self.q
|
||||
h = h*self.u % self.q
|
||||
return h*self.p+m1
|
||||
return pow(c, self.d, self.n)
|
||||
|
||||
def _encrypt(self, m):
|
||||
# compute m**d (mod n)
|
||||
return pow(m, self.e, self.n)
|
||||
|
||||
def _sign(self, m): # alias for _decrypt
|
||||
if not self.has_private():
|
||||
raise TypeError("No private key")
|
||||
return self._decrypt(m)
|
||||
|
||||
def _verify(self, m, sig):
|
||||
return self._encrypt(sig) == m
|
||||
|
||||
def has_private(self):
|
||||
return hasattr(self, 'd')
|
||||
|
||||
def size(self):
|
||||
"""Return the maximum number of bits that can be encrypted"""
|
||||
return size(self.n) - 1
|
||||
|
||||
def rsa_construct(n, e, d=None, p=None, q=None, u=None):
|
||||
"""Construct an RSAKey object"""
|
||||
assert isinstance(n, long)
|
||||
assert isinstance(e, long)
|
||||
assert isinstance(d, (long, type(None)))
|
||||
assert isinstance(p, (long, type(None)))
|
||||
assert isinstance(q, (long, type(None)))
|
||||
assert isinstance(u, (long, type(None)))
|
||||
obj = _RSAKey()
|
||||
obj.n = n
|
||||
obj.e = e
|
||||
if d is None:
|
||||
return obj
|
||||
obj.d = d
|
||||
if p is not None and q is not None:
|
||||
obj.p = p
|
||||
obj.q = q
|
||||
else:
|
||||
# Compute factors p and q from the private exponent d.
|
||||
# We assume that n has no more than two factors.
|
||||
# See 8.2.2(i) in Handbook of Applied Cryptography.
|
||||
ktot = d*e-1
|
||||
# The quantity d*e-1 is a multiple of phi(n), even,
|
||||
# and can be represented as t*2^s.
|
||||
t = ktot
|
||||
while t%2==0:
|
||||
t=divmod(t,2)[0]
|
||||
# Cycle through all multiplicative inverses in Zn.
|
||||
# The algorithm is non-deterministic, but there is a 50% chance
|
||||
# any candidate a leads to successful factoring.
|
||||
# See "Digitalized Signatures and Public Key Functions as Intractable
|
||||
# as Factorization", M. Rabin, 1979
|
||||
spotted = 0
|
||||
a = 2
|
||||
while not spotted and a<100:
|
||||
k = t
|
||||
# Cycle through all values a^{t*2^i}=a^k
|
||||
while k<ktot:
|
||||
cand = pow(a,k,n)
|
||||
# Check if a^k is a non-trivial root of unity (mod n)
|
||||
if cand!=1 and cand!=(n-1) and pow(cand,2,n)==1:
|
||||
# We have found a number such that (cand-1)(cand+1)=0 (mod n).
|
||||
# Either of the terms divides n.
|
||||
obj.p = GCD(cand+1,n)
|
||||
spotted = 1
|
||||
break
|
||||
k = k*2
|
||||
# This value was not any good... let's try another!
|
||||
a = a+2
|
||||
if not spotted:
|
||||
raise ValueError("Unable to compute factors p and q from exponent d.")
|
||||
# Found !
|
||||
assert ((n % obj.p)==0)
|
||||
obj.q = divmod(n,obj.p)[0]
|
||||
if u is not None:
|
||||
obj.u = u
|
||||
else:
|
||||
obj.u = inverse(obj.p, obj.q)
|
||||
return obj
|
||||
|
||||
class _DSAKey(object):
|
||||
def size(self):
|
||||
"""Return the maximum number of bits that can be encrypted"""
|
||||
return size(self.p) - 1
|
||||
|
||||
def has_private(self):
|
||||
return hasattr(self, 'x')
|
||||
|
||||
def _sign(self, m, k): # alias for _decrypt
|
||||
# SECURITY TODO - We _should_ be computing SHA1(m), but we don't because that's the API.
|
||||
if not self.has_private():
|
||||
raise TypeError("No private key")
|
||||
if not (1L < k < self.q):
|
||||
raise ValueError("k is not between 2 and q-1")
|
||||
inv_k = inverse(k, self.q) # Compute k**-1 mod q
|
||||
r = pow(self.g, k, self.p) % self.q # r = (g**k mod p) mod q
|
||||
s = (inv_k * (m + self.x * r)) % self.q
|
||||
return (r, s)
|
||||
|
||||
def _verify(self, m, r, s):
|
||||
# SECURITY TODO - We _should_ be computing SHA1(m), but we don't because that's the API.
|
||||
if not (0 < r < self.q) or not (0 < s < self.q):
|
||||
return False
|
||||
w = inverse(s, self.q)
|
||||
u1 = (m*w) % self.q
|
||||
u2 = (r*w) % self.q
|
||||
v = (pow(self.g, u1, self.p) * pow(self.y, u2, self.p) % self.p) % self.q
|
||||
return v == r
|
||||
|
||||
def dsa_construct(y, g, p, q, x=None):
|
||||
assert isinstance(y, long)
|
||||
assert isinstance(g, long)
|
||||
assert isinstance(p, long)
|
||||
assert isinstance(q, long)
|
||||
assert isinstance(x, (long, type(None)))
|
||||
obj = _DSAKey()
|
||||
obj.y = y
|
||||
obj.g = g
|
||||
obj.p = p
|
||||
obj.q = q
|
||||
if x is not None: obj.x = x
|
||||
return obj
|
||||
|
||||
|
||||
# vim:set ts=4 sw=4 sts=4 expandtab:
|
||||
|
240
modules/Crypto/PublicKey/pubkey.py
Normal file
240
modules/Crypto/PublicKey/pubkey.py
Normal file
@ -0,0 +1,240 @@
|
||||
#
|
||||
# pubkey.py : Internal functions for public key operations
|
||||
#
|
||||
# Part of the Python Cryptography Toolkit
|
||||
#
|
||||
# Written by Andrew Kuchling, Paul Swartz, and others
|
||||
#
|
||||
# ===================================================================
|
||||
# The contents of this file are dedicated to the public domain. To
|
||||
# the extent that dedication to the public domain is not available,
|
||||
# everyone is granted a worldwide, perpetual, royalty-free,
|
||||
# non-exclusive license to exercise all rights associated with the
|
||||
# contents of this file for any purpose whatsoever.
|
||||
# No rights are reserved.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
# SOFTWARE.
|
||||
# ===================================================================
|
||||
#
|
||||
|
||||
__revision__ = "$Id$"
|
||||
|
||||
import types, warnings
|
||||
from Crypto.Util.number import *
|
||||
|
||||
# Basic public key class
|
||||
class pubkey:
|
||||
"""An abstract class for a public key object.
|
||||
|
||||
:undocumented: __getstate__, __setstate__, __eq__, __ne__, validate
|
||||
"""
|
||||
def __init__(self):
|
||||
pass
|
||||
|
||||
def __getstate__(self):
|
||||
"""To keep key objects platform-independent, the key data is
|
||||
converted to standard Python long integers before being
|
||||
written out. It will then be reconverted as necessary on
|
||||
restoration."""
|
||||
d=self.__dict__
|
||||
for key in self.keydata:
|
||||
if d.has_key(key): d[key]=long(d[key])
|
||||
return d
|
||||
|
||||
def __setstate__(self, d):
|
||||
"""On unpickling a key object, the key data is converted to the big
|
||||
number representation being used, whether that is Python long
|
||||
integers, MPZ objects, or whatever."""
|
||||
for key in self.keydata:
|
||||
if d.has_key(key): self.__dict__[key]=bignum(d[key])
|
||||
|
||||
def encrypt(self, plaintext, K):
|
||||
"""Encrypt a piece of data.
|
||||
|
||||
:Parameter plaintext: The piece of data to encrypt.
|
||||
:Type plaintext: byte string or long
|
||||
|
||||
:Parameter K: A random parameter required by some algorithms
|
||||
:Type K: byte string or long
|
||||
|
||||
:Return: A tuple with two items. Each item is of the same type as the
|
||||
plaintext (string or long).
|
||||
"""
|
||||
wasString=0
|
||||
if isinstance(plaintext, types.StringType):
|
||||
plaintext=bytes_to_long(plaintext) ; wasString=1
|
||||
if isinstance(K, types.StringType):
|
||||
K=bytes_to_long(K)
|
||||
ciphertext=self._encrypt(plaintext, K)
|
||||
if wasString: return tuple(map(long_to_bytes, ciphertext))
|
||||
else: return ciphertext
|
||||
|
||||
def decrypt(self, ciphertext):
|
||||
"""Decrypt a piece of data.
|
||||
|
||||
:Parameter ciphertext: The piece of data to decrypt.
|
||||
:Type ciphertext: byte string, long or a 2-item tuple as returned by `encrypt`
|
||||
|
||||
:Return: A byte string if ciphertext was a byte string or a tuple
|
||||
of byte strings. A long otherwise.
|
||||
"""
|
||||
wasString=0
|
||||
if not isinstance(ciphertext, types.TupleType):
|
||||
ciphertext=(ciphertext,)
|
||||
if isinstance(ciphertext[0], types.StringType):
|
||||
ciphertext=tuple(map(bytes_to_long, ciphertext)) ; wasString=1
|
||||
plaintext=self._decrypt(ciphertext)
|
||||
if wasString: return long_to_bytes(plaintext)
|
||||
else: return plaintext
|
||||
|
||||
def sign(self, M, K):
|
||||
"""Sign a piece of data.
|
||||
|
||||
:Parameter M: The piece of data to encrypt.
|
||||
:Type M: byte string or long
|
||||
|
||||
:Parameter K: A random parameter required by some algorithms
|
||||
:Type K: byte string or long
|
||||
|
||||
:Return: A tuple with two items.
|
||||
"""
|
||||
if (not self.has_private()):
|
||||
raise TypeError('Private key not available in this object')
|
||||
if isinstance(M, types.StringType): M=bytes_to_long(M)
|
||||
if isinstance(K, types.StringType): K=bytes_to_long(K)
|
||||
return self._sign(M, K)
|
||||
|
||||
def verify (self, M, signature):
|
||||
"""Verify the validity of a signature.
|
||||
|
||||
:Parameter M: The expected message.
|
||||
:Type M: byte string or long
|
||||
|
||||
:Parameter signature: The signature to verify.
|
||||
:Type signature: tuple with two items, as return by `sign`
|
||||
|
||||
:Return: True if the signature is correct, False otherwise.
|
||||
"""
|
||||
if isinstance(M, types.StringType): M=bytes_to_long(M)
|
||||
return self._verify(M, signature)
|
||||
|
||||
# alias to compensate for the old validate() name
|
||||
def validate (self, M, signature):
|
||||
warnings.warn("validate() method name is obsolete; use verify()",
|
||||
DeprecationWarning)
|
||||
|
||||
def blind(self, M, B):
|
||||
"""Blind a message to prevent certain side-channel attacks.
|
||||
|
||||
:Parameter M: The message to blind.
|
||||
:Type M: byte string or long
|
||||
|
||||
:Parameter B: Blinding factor.
|
||||
:Type B: byte string or long
|
||||
|
||||
:Return: A byte string if M was so. A long otherwise.
|
||||
"""
|
||||
wasString=0
|
||||
if isinstance(M, types.StringType):
|
||||
M=bytes_to_long(M) ; wasString=1
|
||||
if isinstance(B, types.StringType): B=bytes_to_long(B)
|
||||
blindedmessage=self._blind(M, B)
|
||||
if wasString: return long_to_bytes(blindedmessage)
|
||||
else: return blindedmessage
|
||||
|
||||
def unblind(self, M, B):
|
||||
"""Unblind a message after cryptographic processing.
|
||||
|
||||
:Parameter M: The encoded message to unblind.
|
||||
:Type M: byte string or long
|
||||
|
||||
:Parameter B: Blinding factor.
|
||||
:Type B: byte string or long
|
||||
"""
|
||||
wasString=0
|
||||
if isinstance(M, types.StringType):
|
||||
M=bytes_to_long(M) ; wasString=1
|
||||
if isinstance(B, types.StringType): B=bytes_to_long(B)
|
||||
unblindedmessage=self._unblind(M, B)
|
||||
if wasString: return long_to_bytes(unblindedmessage)
|
||||
else: return unblindedmessage
|
||||
|
||||
|
||||
# The following methods will usually be left alone, except for
|
||||
# signature-only algorithms. They both return Boolean values
|
||||
# recording whether this key's algorithm can sign and encrypt.
|
||||
def can_sign (self):
|
||||
"""Tell if the algorithm can deal with cryptographic signatures.
|
||||
|
||||
This property concerns the *algorithm*, not the key itself.
|
||||
It may happen that this particular key object hasn't got
|
||||
the private information required to generate a signature.
|
||||
|
||||
:Return: boolean
|
||||
"""
|
||||
return 1
|
||||
|
||||
def can_encrypt (self):
|
||||
"""Tell if the algorithm can deal with data encryption.
|
||||
|
||||
This property concerns the *algorithm*, not the key itself.
|
||||
It may happen that this particular key object hasn't got
|
||||
the private information required to decrypt data.
|
||||
|
||||
:Return: boolean
|
||||
"""
|
||||
return 1
|
||||
|
||||
def can_blind (self):
|
||||
"""Tell if the algorithm can deal with data blinding.
|
||||
|
||||
This property concerns the *algorithm*, not the key itself.
|
||||
It may happen that this particular key object hasn't got
|
||||
the private information required carry out blinding.
|
||||
|
||||
:Return: boolean
|
||||
"""
|
||||
return 0
|
||||
|
||||
# The following methods will certainly be overridden by
|
||||
# subclasses.
|
||||
|
||||
def size (self):
|
||||
"""Tell the maximum number of bits that can be handled by this key.
|
||||
|
||||
:Return: int
|
||||
"""
|
||||
return 0
|
||||
|
||||
def has_private (self):
|
||||
"""Tell if the key object contains private components.
|
||||
|
||||
:Return: bool
|
||||
"""
|
||||
return 0
|
||||
|
||||
def publickey (self):
|
||||
"""Construct a new key carrying only the public information.
|
||||
|
||||
:Return: A new `pubkey` object.
|
||||
"""
|
||||
return self
|
||||
|
||||
def __eq__ (self, other):
|
||||
"""__eq__(other): 0, 1
|
||||
Compare us to other for equality.
|
||||
"""
|
||||
return self.__getstate__() == other.__getstate__()
|
||||
|
||||
def __ne__ (self, other):
|
||||
"""__ne__(other): 0, 1
|
||||
Compare us to other for inequality.
|
||||
"""
|
||||
return not self.__eq__(other)
|
174
modules/Crypto/Random/Fortuna/FortunaAccumulator.py
Normal file
174
modules/Crypto/Random/Fortuna/FortunaAccumulator.py
Normal file
@ -0,0 +1,174 @@
|
||||
# -*- coding: ascii -*-
|
||||
#
|
||||
# FortunaAccumulator.py : Fortuna's internal accumulator
|
||||
#
|
||||
# Written in 2008 by Dwayne C. Litzenberger <dlitz@dlitz.net>
|
||||
#
|
||||
# ===================================================================
|
||||
# The contents of this file are dedicated to the public domain. To
|
||||
# the extent that dedication to the public domain is not available,
|
||||
# everyone is granted a worldwide, perpetual, royalty-free,
|
||||
# non-exclusive license to exercise all rights associated with the
|
||||
# contents of this file for any purpose whatsoever.
|
||||
# No rights are reserved.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
# SOFTWARE.
|
||||
# ===================================================================
|
||||
|
||||
__revision__ = "$Id$"
|
||||
|
||||
import sys
|
||||
if sys.version_info[0] == 2 and sys.version_info[1] == 1:
|
||||
from Crypto.Util.py21compat import *
|
||||
from Crypto.Util.py3compat import *
|
||||
|
||||
from binascii import b2a_hex
|
||||
import time
|
||||
import warnings
|
||||
|
||||
from Crypto.pct_warnings import ClockRewindWarning
|
||||
import SHAd256
|
||||
|
||||
# If the system has monotonic time, we'll use it.
|
||||
from Crypto.Util._time import maybe_monotonic_time
|
||||
|
||||
import FortunaGenerator
|
||||
|
||||
class FortunaPool(object):
|
||||
"""Fortuna pool type
|
||||
|
||||
This object acts like a hash object, with the following differences:
|
||||
|
||||
- It keeps a count (the .length attribute) of the number of bytes that
|
||||
have been added to the pool
|
||||
- It supports a .reset() method for in-place reinitialization
|
||||
- The method to add bytes to the pool is .append(), not .update().
|
||||
"""
|
||||
|
||||
digest_size = SHAd256.digest_size
|
||||
|
||||
def __init__(self):
|
||||
self.reset()
|
||||
|
||||
def append(self, data):
|
||||
self._h.update(data)
|
||||
self.length += len(data)
|
||||
|
||||
def digest(self):
|
||||
return self._h.digest()
|
||||
|
||||
def hexdigest(self):
|
||||
if sys.version_info[0] == 2:
|
||||
return b2a_hex(self.digest())
|
||||
else:
|
||||
return b2a_hex(self.digest()).decode()
|
||||
|
||||
def reset(self):
|
||||
self._h = SHAd256.new()
|
||||
self.length = 0
|
||||
|
||||
def which_pools(r):
|
||||
"""Return a list of pools indexes (in range(32)) that are to be included during reseed number r.
|
||||
|
||||
According to _Practical Cryptography_, chapter 10.5.2 "Pools":
|
||||
|
||||
"Pool P_i is included if 2**i is a divisor of r. Thus P_0 is used
|
||||
every reseed, P_1 every other reseed, P_2 every fourth reseed, etc."
|
||||
"""
|
||||
# This is a separate function so that it can be unit-tested.
|
||||
assert r >= 1
|
||||
retval = []
|
||||
mask = 0
|
||||
for i in range(32):
|
||||
# "Pool P_i is included if 2**i is a divisor of [reseed_count]"
|
||||
if (r & mask) == 0:
|
||||
retval.append(i)
|
||||
else:
|
||||
break # optimization. once this fails, it always fails
|
||||
mask = (mask << 1) | 1L
|
||||
return retval
|
||||
|
||||
class FortunaAccumulator(object):
|
||||
|
||||
# An estimate of how many bytes we must append to pool 0 before it will
|
||||
# contain 128 bits of entropy (with respect to an attack). We reseed the
|
||||
# generator only after pool 0 contains `min_pool_size` bytes. Note that
|
||||
# unlike with some other PRNGs, Fortuna's security does not rely on the
|
||||
# accuracy of this estimate---we can accord to be optimistic here.
|
||||
min_pool_size = 64 # size in bytes
|
||||
|
||||
# If an attacker can predict some (but not all) of our entropy sources, the
|
||||
# `min_pool_size` check may not be sufficient to prevent a successful state
|
||||
# compromise extension attack. To resist this attack, Fortuna spreads the
|
||||
# input across 32 pools, which are then consumed (to reseed the output
|
||||
# generator) with exponentially decreasing frequency.
|
||||
#
|
||||
# In order to prevent an attacker from gaining knowledge of all 32 pools
|
||||
# before we have a chance to fill them with enough information that the
|
||||
# attacker cannot predict, we impose a rate limit of 10 reseeds/second (one
|
||||
# per 100 ms). This ensures that a hypothetical 33rd pool would only be
|
||||
# needed after a minimum of 13 years of sustained attack.
|
||||
reseed_interval = 0.100 # time in seconds
|
||||
|
||||
def __init__(self):
|
||||
self.reseed_count = 0
|
||||
self.generator = FortunaGenerator.AESGenerator()
|
||||
self.last_reseed = None
|
||||
|
||||
# Initialize 32 FortunaPool instances.
|
||||
# NB: This is _not_ equivalent to [FortunaPool()]*32, which would give
|
||||
# us 32 references to the _same_ FortunaPool instance (and cause the
|
||||
# assertion below to fail).
|
||||
self.pools = [FortunaPool() for i in range(32)] # 32 pools
|
||||
assert(self.pools[0] is not self.pools[1])
|
||||
|
||||
def _forget_last_reseed(self):
|
||||
# This is not part of the standard Fortuna definition, and using this
|
||||
# function frequently can weaken Fortuna's ability to resist a state
|
||||
# compromise extension attack, but we need this in order to properly
|
||||
# implement Crypto.Random.atfork(). Otherwise, forked child processes
|
||||
# might continue to use their parent's PRNG state for up to 100ms in
|
||||
# some cases. (e.g. CVE-2013-1445)
|
||||
self.last_reseed = None
|
||||
|
||||
def random_data(self, bytes):
|
||||
current_time = maybe_monotonic_time()
|
||||
if (self.last_reseed is not None and self.last_reseed > current_time): # Avoid float comparison to None to make Py3k happy
|
||||
warnings.warn("Clock rewind detected. Resetting last_reseed.", ClockRewindWarning)
|
||||
self.last_reseed = None
|
||||
if (self.pools[0].length >= self.min_pool_size and
|
||||
(self.last_reseed is None or
|
||||
current_time > self.last_reseed + self.reseed_interval)):
|
||||
self._reseed(current_time)
|
||||
# The following should fail if we haven't seeded the pool yet.
|
||||
return self.generator.pseudo_random_data(bytes)
|
||||
|
||||
def _reseed(self, current_time=None):
|
||||
if current_time is None:
|
||||
current_time = maybe_monotonic_time()
|
||||
seed = []
|
||||
self.reseed_count += 1
|
||||
self.last_reseed = current_time
|
||||
for i in which_pools(self.reseed_count):
|
||||
seed.append(self.pools[i].digest())
|
||||
self.pools[i].reset()
|
||||
|
||||
seed = b("").join(seed)
|
||||
self.generator.reseed(seed)
|
||||
|
||||
def add_random_event(self, source_number, pool_number, data):
|
||||
assert 1 <= len(data) <= 32
|
||||
assert 0 <= source_number <= 255
|
||||
assert 0 <= pool_number <= 31
|
||||
self.pools[pool_number].append(bchr(source_number))
|
||||
self.pools[pool_number].append(bchr(len(data)))
|
||||
self.pools[pool_number].append(data)
|
||||
|
||||
# vim:set ts=4 sw=4 sts=4 expandtab:
|
132
modules/Crypto/Random/Fortuna/FortunaGenerator.py
Normal file
132
modules/Crypto/Random/Fortuna/FortunaGenerator.py
Normal file
@ -0,0 +1,132 @@
|
||||
# -*- coding: ascii -*-
|
||||
#
|
||||
# FortunaGenerator.py : Fortuna's internal PRNG
|
||||
#
|
||||
# Written in 2008 by Dwayne C. Litzenberger <dlitz@dlitz.net>
|
||||
#
|
||||
# ===================================================================
|
||||
# The contents of this file are dedicated to the public domain. To
|
||||
# the extent that dedication to the public domain is not available,
|
||||
# everyone is granted a worldwide, perpetual, royalty-free,
|
||||
# non-exclusive license to exercise all rights associated with the
|
||||
# contents of this file for any purpose whatsoever.
|
||||
# No rights are reserved.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
# SOFTWARE.
|
||||
# ===================================================================
|
||||
|
||||
__revision__ = "$Id$"
|
||||
|
||||
import sys
|
||||
if sys.version_info[0] == 2 and sys.version_info[1] == 1:
|
||||
from Crypto.Util.py21compat import *
|
||||
from Crypto.Util.py3compat import *
|
||||
|
||||
import struct
|
||||
|
||||
from Crypto.Util.number import ceil_shift, exact_log2, exact_div
|
||||
from Crypto.Util import Counter
|
||||
from Crypto.Cipher import AES
|
||||
|
||||
import SHAd256
|
||||
|
||||
class AESGenerator(object):
|
||||
"""The Fortuna "generator"
|
||||
|
||||
This is used internally by the Fortuna PRNG to generate arbitrary amounts
|
||||
of pseudorandom data from a smaller amount of seed data.
|
||||
|
||||
The output is generated by running AES-256 in counter mode and re-keying
|
||||
after every mebibyte (2**16 blocks) of output.
|
||||
"""
|
||||
|
||||
block_size = AES.block_size # output block size in octets (128 bits)
|
||||
key_size = 32 # key size in octets (256 bits)
|
||||
|
||||
# Because of the birthday paradox, we expect to find approximately one
|
||||
# collision for every 2**64 blocks of output from a real random source.
|
||||
# However, this code generates pseudorandom data by running AES in
|
||||
# counter mode, so there will be no collisions until the counter
|
||||
# (theoretically) wraps around at 2**128 blocks. Thus, in order to prevent
|
||||
# Fortuna's pseudorandom output from deviating perceptibly from a true
|
||||
# random source, Ferguson and Schneier specify a limit of 2**16 blocks
|
||||
# without rekeying.
|
||||
max_blocks_per_request = 2**16 # Allow no more than this number of blocks per _pseudo_random_data request
|
||||
|
||||
_four_kiblocks_of_zeros = b("\0") * block_size * 4096
|
||||
|
||||
def __init__(self):
|
||||
self.counter = Counter.new(nbits=self.block_size*8, initial_value=0, little_endian=True)
|
||||
self.key = None
|
||||
|
||||
# Set some helper constants
|
||||
self.block_size_shift = exact_log2(self.block_size)
|
||||
assert (1 << self.block_size_shift) == self.block_size
|
||||
|
||||
self.blocks_per_key = exact_div(self.key_size, self.block_size)
|
||||
assert self.key_size == self.blocks_per_key * self.block_size
|
||||
|
||||
self.max_bytes_per_request = self.max_blocks_per_request * self.block_size
|
||||
|
||||
def reseed(self, seed):
|
||||
if self.key is None:
|
||||
self.key = b("\0") * self.key_size
|
||||
|
||||
self._set_key(SHAd256.new(self.key + seed).digest())
|
||||
self.counter() # increment counter
|
||||
assert len(self.key) == self.key_size
|
||||
|
||||
def pseudo_random_data(self, bytes):
|
||||
assert bytes >= 0
|
||||
|
||||
num_full_blocks = bytes >> 20
|
||||
remainder = bytes & ((1<<20)-1)
|
||||
|
||||
retval = []
|
||||
for i in xrange(num_full_blocks):
|
||||
retval.append(self._pseudo_random_data(1<<20))
|
||||
retval.append(self._pseudo_random_data(remainder))
|
||||
|
||||
return b("").join(retval)
|
||||
|
||||
def _set_key(self, key):
|
||||
self.key = key
|
||||
self._cipher = AES.new(key, AES.MODE_CTR, counter=self.counter)
|
||||
|
||||
def _pseudo_random_data(self, bytes):
|
||||
if not (0 <= bytes <= self.max_bytes_per_request):
|
||||
raise AssertionError("You cannot ask for more than 1 MiB of data per request")
|
||||
|
||||
num_blocks = ceil_shift(bytes, self.block_size_shift) # num_blocks = ceil(bytes / self.block_size)
|
||||
|
||||
# Compute the output
|
||||
retval = self._generate_blocks(num_blocks)[:bytes]
|
||||
|
||||
# Switch to a new key to avoid later compromises of this output (i.e.
|
||||
# state compromise extension attacks)
|
||||
self._set_key(self._generate_blocks(self.blocks_per_key))
|
||||
|
||||
assert len(retval) == bytes
|
||||
assert len(self.key) == self.key_size
|
||||
|
||||
return retval
|
||||
|
||||
def _generate_blocks(self, num_blocks):
|
||||
if self.key is None:
|
||||
raise AssertionError("generator must be seeded before use")
|
||||
assert 0 <= num_blocks <= self.max_blocks_per_request
|
||||
retval = []
|
||||
for i in xrange(num_blocks >> 12): # xrange(num_blocks / 4096)
|
||||
retval.append(self._cipher.encrypt(self._four_kiblocks_of_zeros))
|
||||
remaining_bytes = (num_blocks & 4095) << self.block_size_shift # (num_blocks % 4095) * self.block_size
|
||||
retval.append(self._cipher.encrypt(self._four_kiblocks_of_zeros[:remaining_bytes]))
|
||||
return b("").join(retval)
|
||||
|
||||
# vim:set ts=4 sw=4 sts=4 expandtab:
|
98
modules/Crypto/Random/Fortuna/SHAd256.py
Normal file
98
modules/Crypto/Random/Fortuna/SHAd256.py
Normal file
@ -0,0 +1,98 @@
|
||||
# -*- coding: ascii -*-
|
||||
#
|
||||
# Random/Fortuna/SHAd256.py : SHA_d-256 hash function implementation
|
||||
#
|
||||
# Written in 2008 by Dwayne C. Litzenberger <dlitz@dlitz.net>
|
||||
#
|
||||
# ===================================================================
|
||||
# The contents of this file are dedicated to the public domain. To
|
||||
# the extent that dedication to the public domain is not available,
|
||||
# everyone is granted a worldwide, perpetual, royalty-free,
|
||||
# non-exclusive license to exercise all rights associated with the
|
||||
# contents of this file for any purpose whatsoever.
|
||||
# No rights are reserved.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
# SOFTWARE.
|
||||
# ===================================================================
|
||||
|
||||
"""\
|
||||
SHA_d-256 hash function implementation.
|
||||
|
||||
This module should comply with PEP 247.
|
||||
"""
|
||||
|
||||
__revision__ = "$Id$"
|
||||
__all__ = ['new', 'digest_size']
|
||||
|
||||
import sys
|
||||
if sys.version_info[0] == 2 and sys.version_info[1] == 1:
|
||||
from Crypto.Util.py21compat import *
|
||||
from Crypto.Util.py3compat import *
|
||||
|
||||
from binascii import b2a_hex
|
||||
|
||||
from Crypto.Hash import SHA256
|
||||
|
||||
assert SHA256.digest_size == 32
|
||||
|
||||
class _SHAd256(object):
|
||||
"""SHA-256, doubled.
|
||||
|
||||
Returns SHA-256(SHA-256(data)).
|
||||
"""
|
||||
|
||||
digest_size = SHA256.digest_size
|
||||
|
||||
_internal = object()
|
||||
|
||||
def __init__(self, internal_api_check, sha256_hash_obj):
|
||||
if internal_api_check is not self._internal:
|
||||
raise AssertionError("Do not instantiate this class directly. Use %s.new()" % (__name__,))
|
||||
self._h = sha256_hash_obj
|
||||
|
||||
# PEP 247 "copy" method
|
||||
def copy(self):
|
||||
"""Return a copy of this hashing object"""
|
||||
return _SHAd256(SHAd256._internal, self._h.copy())
|
||||
|
||||
# PEP 247 "digest" method
|
||||
def digest(self):
|
||||
"""Return the hash value of this object as a binary string"""
|
||||
retval = SHA256.new(self._h.digest()).digest()
|
||||
assert len(retval) == 32
|
||||
return retval
|
||||
|
||||
# PEP 247 "hexdigest" method
|
||||
def hexdigest(self):
|
||||
"""Return the hash value of this object as a (lowercase) hexadecimal string"""
|
||||
retval = b2a_hex(self.digest())
|
||||
assert len(retval) == 64
|
||||
if sys.version_info[0] == 2:
|
||||
return retval
|
||||
else:
|
||||
return retval.decode()
|
||||
|
||||
# PEP 247 "update" method
|
||||
def update(self, data):
|
||||
self._h.update(data)
|
||||
|
||||
# PEP 247 module-level "digest_size" variable
|
||||
digest_size = _SHAd256.digest_size
|
||||
|
||||
# PEP 247 module-level "new" function
|
||||
def new(data=None):
|
||||
"""Return a new SHAd256 hashing object"""
|
||||
if not data:
|
||||
data=b("")
|
||||
sha = _SHAd256(_SHAd256._internal, SHA256.new(data))
|
||||
sha.new = globals()['new']
|
||||
return sha
|
||||
|
||||
# vim:set ts=4 sw=4 sts=4 expandtab:
|
0
modules/Crypto/Random/Fortuna/__init__.py
Normal file
0
modules/Crypto/Random/Fortuna/__init__.py
Normal file
40
modules/Crypto/Random/OSRNG/__init__.py
Normal file
40
modules/Crypto/Random/OSRNG/__init__.py
Normal file
@ -0,0 +1,40 @@
|
||||
#
|
||||
# Random/OSRNG/__init__.py : Platform-independent OS RNG API
|
||||
#
|
||||
# Written in 2008 by Dwayne C. Litzenberger <dlitz@dlitz.net>
|
||||
#
|
||||
# ===================================================================
|
||||
# The contents of this file are dedicated to the public domain. To
|
||||
# the extent that dedication to the public domain is not available,
|
||||
# everyone is granted a worldwide, perpetual, royalty-free,
|
||||
# non-exclusive license to exercise all rights associated with the
|
||||
# contents of this file for any purpose whatsoever.
|
||||
# No rights are reserved.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
# SOFTWARE.
|
||||
# ===================================================================
|
||||
|
||||
"""Provides a platform-independent interface to the random number generators
|
||||
supplied by various operating systems."""
|
||||
|
||||
__revision__ = "$Id$"
|
||||
|
||||
import os
|
||||
|
||||
if os.name == 'posix':
|
||||
from Crypto.Random.OSRNG.posix import new
|
||||
elif os.name == 'nt':
|
||||
from Crypto.Random.OSRNG.nt import new
|
||||
elif hasattr(os, 'urandom'):
|
||||
from Crypto.Random.OSRNG.fallback import new
|
||||
else:
|
||||
raise ImportError("Not implemented")
|
||||
|
||||
# vim:set ts=4 sw=4 sts=4 expandtab:
|
46
modules/Crypto/Random/OSRNG/fallback.py
Normal file
46
modules/Crypto/Random/OSRNG/fallback.py
Normal file
@ -0,0 +1,46 @@
|
||||
#
|
||||
# Random/OSRNG/fallback.py : Fallback entropy source for systems with os.urandom
|
||||
#
|
||||
# Written in 2008 by Dwayne C. Litzenberger <dlitz@dlitz.net>
|
||||
#
|
||||
# ===================================================================
|
||||
# The contents of this file are dedicated to the public domain. To
|
||||
# the extent that dedication to the public domain is not available,
|
||||
# everyone is granted a worldwide, perpetual, royalty-free,
|
||||
# non-exclusive license to exercise all rights associated with the
|
||||
# contents of this file for any purpose whatsoever.
|
||||
# No rights are reserved.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
# SOFTWARE.
|
||||
# ===================================================================
|
||||
|
||||
|
||||
__revision__ = "$Id$"
|
||||
__all__ = ['PythonOSURandomRNG']
|
||||
|
||||
import os
|
||||
|
||||
from rng_base import BaseRNG
|
||||
|
||||
class PythonOSURandomRNG(BaseRNG):
|
||||
|
||||
name = "<os.urandom>"
|
||||
|
||||
def __init__(self):
|
||||
self._read = os.urandom
|
||||
BaseRNG.__init__(self)
|
||||
|
||||
def _close(self):
|
||||
self._read = None
|
||||
|
||||
def new(*args, **kwargs):
|
||||
return PythonOSURandomRNG(*args, **kwargs)
|
||||
|
||||
# vim:set ts=4 sw=4 sts=4 expandtab:
|
74
modules/Crypto/Random/OSRNG/nt.py
Normal file
74
modules/Crypto/Random/OSRNG/nt.py
Normal file
@ -0,0 +1,74 @@
|
||||
#
|
||||
# Random/OSRNG/nt.py : OS entropy source for MS Windows
|
||||
#
|
||||
# Written in 2008 by Dwayne C. Litzenberger <dlitz@dlitz.net>
|
||||
#
|
||||
# ===================================================================
|
||||
# The contents of this file are dedicated to the public domain. To
|
||||
# the extent that dedication to the public domain is not available,
|
||||
# everyone is granted a worldwide, perpetual, royalty-free,
|
||||
# non-exclusive license to exercise all rights associated with the
|
||||
# contents of this file for any purpose whatsoever.
|
||||
# No rights are reserved.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
# SOFTWARE.
|
||||
# ===================================================================
|
||||
|
||||
|
||||
__revision__ = "$Id$"
|
||||
__all__ = ['WindowsRNG']
|
||||
|
||||
from Crypto.Random.OSRNG import winrandom
|
||||
from rng_base import BaseRNG
|
||||
|
||||
class WindowsRNG(BaseRNG):
|
||||
|
||||
name = "<CryptGenRandom>"
|
||||
|
||||
def __init__(self):
|
||||
self.__winrand = winrandom.new()
|
||||
BaseRNG.__init__(self)
|
||||
|
||||
def flush(self):
|
||||
"""Work around weakness in Windows RNG.
|
||||
|
||||
The CryptGenRandom mechanism in some versions of Windows allows an
|
||||
attacker to learn 128 KiB of past and future output. As a workaround,
|
||||
this function reads 128 KiB of 'random' data from Windows and discards
|
||||
it.
|
||||
|
||||
For more information about the weaknesses in CryptGenRandom, see
|
||||
_Cryptanalysis of the Random Number Generator of the Windows Operating
|
||||
System_, by Leo Dorrendorf and Zvi Gutterman and Benny Pinkas
|
||||
http://eprint.iacr.org/2007/419
|
||||
"""
|
||||
if self.closed:
|
||||
raise ValueError("I/O operation on closed file")
|
||||
data = self.__winrand.get_bytes(128*1024)
|
||||
assert (len(data) == 128*1024)
|
||||
BaseRNG.flush(self)
|
||||
|
||||
def _close(self):
|
||||
self.__winrand = None
|
||||
|
||||
def _read(self, N):
|
||||
# Unfortunately, research shows that CryptGenRandom doesn't provide
|
||||
# forward secrecy and fails the next-bit test unless we apply a
|
||||
# workaround, which we do here. See http://eprint.iacr.org/2007/419
|
||||
# for information on the vulnerability.
|
||||
self.flush()
|
||||
data = self.__winrand.get_bytes(N)
|
||||
self.flush()
|
||||
return data
|
||||
|
||||
def new(*args, **kwargs):
|
||||
return WindowsRNG(*args, **kwargs)
|
||||
|
||||
# vim:set ts=4 sw=4 sts=4 expandtab:
|
86
modules/Crypto/Random/OSRNG/posix.py
Normal file
86
modules/Crypto/Random/OSRNG/posix.py
Normal file
@ -0,0 +1,86 @@
|
||||
#
|
||||
# Random/OSRNG/posix.py : OS entropy source for POSIX systems
|
||||
#
|
||||
# Written in 2008 by Dwayne C. Litzenberger <dlitz@dlitz.net>
|
||||
#
|
||||
# ===================================================================
|
||||
# The contents of this file are dedicated to the public domain. To
|
||||
# the extent that dedication to the public domain is not available,
|
||||
# everyone is granted a worldwide, perpetual, royalty-free,
|
||||
# non-exclusive license to exercise all rights associated with the
|
||||
# contents of this file for any purpose whatsoever.
|
||||
# No rights are reserved.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
# SOFTWARE.
|
||||
# ===================================================================
|
||||
|
||||
|
||||
__revision__ = "$Id$"
|
||||
__all__ = ['DevURandomRNG']
|
||||
|
||||
import errno
|
||||
import os
|
||||
import stat
|
||||
|
||||
from rng_base import BaseRNG
|
||||
from Crypto.Util.py3compat import b
|
||||
|
||||
class DevURandomRNG(BaseRNG):
|
||||
|
||||
def __init__(self, devname=None):
|
||||
if devname is None:
|
||||
self.name = "/dev/urandom"
|
||||
else:
|
||||
self.name = devname
|
||||
|
||||
# Test that /dev/urandom is a character special device
|
||||
f = open(self.name, "rb", 0)
|
||||
fmode = os.fstat(f.fileno())[stat.ST_MODE]
|
||||
if not stat.S_ISCHR(fmode):
|
||||
f.close()
|
||||
raise TypeError("%r is not a character special device" % (self.name,))
|
||||
|
||||
self.__file = f
|
||||
|
||||
BaseRNG.__init__(self)
|
||||
|
||||
def _close(self):
|
||||
self.__file.close()
|
||||
|
||||
def _read(self, N):
|
||||
# Starting with Python 3 open with buffering=0 returns a FileIO object.
|
||||
# FileIO.read behaves like read(2) and not like fread(3) and thus we
|
||||
# have to handle the case that read returns less data as requested here
|
||||
# more carefully.
|
||||
data = b("")
|
||||
while len(data) < N:
|
||||
try:
|
||||
d = self.__file.read(N - len(data))
|
||||
except IOError, e:
|
||||
# read(2) has been interrupted by a signal; redo the read
|
||||
if e.errno == errno.EINTR:
|
||||
continue
|
||||
raise
|
||||
|
||||
if d is None:
|
||||
# __file is in non-blocking mode and no data is available
|
||||
return data
|
||||
if len(d) == 0:
|
||||
# __file is in blocking mode and arrived at EOF
|
||||
return data
|
||||
|
||||
data += d
|
||||
return data
|
||||
|
||||
def new(*args, **kwargs):
|
||||
return DevURandomRNG(*args, **kwargs)
|
||||
|
||||
|
||||
# vim:set ts=4 sw=4 sts=4 expandtab:
|
88
modules/Crypto/Random/OSRNG/rng_base.py
Normal file
88
modules/Crypto/Random/OSRNG/rng_base.py
Normal file
@ -0,0 +1,88 @@
|
||||
#
|
||||
# Random/OSRNG/rng_base.py : Base class for OSRNG
|
||||
#
|
||||
# Written in 2008 by Dwayne C. Litzenberger <dlitz@dlitz.net>
|
||||
#
|
||||
# ===================================================================
|
||||
# The contents of this file are dedicated to the public domain. To
|
||||
# the extent that dedication to the public domain is not available,
|
||||
# everyone is granted a worldwide, perpetual, royalty-free,
|
||||
# non-exclusive license to exercise all rights associated with the
|
||||
# contents of this file for any purpose whatsoever.
|
||||
# No rights are reserved.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
# SOFTWARE.
|
||||
# ===================================================================
|
||||
|
||||
__revision__ = "$Id$"
|
||||
|
||||
import sys
|
||||
if sys.version_info[0] == 2 and sys.version_info[1] == 1:
|
||||
from Crypto.Util.py21compat import *
|
||||
|
||||
class BaseRNG(object):
|
||||
|
||||
def __init__(self):
|
||||
self.closed = False
|
||||
self._selftest()
|
||||
|
||||
def __del__(self):
|
||||
self.close()
|
||||
|
||||
def _selftest(self):
|
||||
# Test that urandom can return data
|
||||
data = self.read(16)
|
||||
if len(data) != 16:
|
||||
raise AssertionError("read truncated")
|
||||
|
||||
# Test that we get different data every time (if we don't, the RNG is
|
||||
# probably malfunctioning)
|
||||
data2 = self.read(16)
|
||||
if data == data2:
|
||||
raise AssertionError("OS RNG returned duplicate data")
|
||||
|
||||
# PEP 343: Support for the "with" statement
|
||||
def __enter__(self):
|
||||
pass
|
||||
def __exit__(self):
|
||||
"""PEP 343 support"""
|
||||
self.close()
|
||||
|
||||
def close(self):
|
||||
if not self.closed:
|
||||
self._close()
|
||||
self.closed = True
|
||||
|
||||
def flush(self):
|
||||
pass
|
||||
|
||||
def read(self, N=-1):
|
||||
"""Return N bytes from the RNG."""
|
||||
if self.closed:
|
||||
raise ValueError("I/O operation on closed file")
|
||||
if not isinstance(N, (long, int)):
|
||||
raise TypeError("an integer is required")
|
||||
if N < 0:
|
||||
raise ValueError("cannot read to end of infinite stream")
|
||||
elif N == 0:
|
||||
return ""
|
||||
data = self._read(N)
|
||||
if len(data) != N:
|
||||
raise AssertionError("%s produced truncated output (requested %d, got %d)" % (self.name, N, len(data)))
|
||||
return data
|
||||
|
||||
def _close(self):
|
||||
raise NotImplementedError("child class must implement this")
|
||||
|
||||
def _read(self, N):
|
||||
raise NotImplementedError("child class must implement this")
|
||||
|
||||
|
||||
# vim:set ts=4 sw=4 sts=4 expandtab:
|
230
modules/Crypto/Random/_UserFriendlyRNG.py
Normal file
230
modules/Crypto/Random/_UserFriendlyRNG.py
Normal file
@ -0,0 +1,230 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Random/_UserFriendlyRNG.py : A user-friendly random number generator
|
||||
#
|
||||
# Written in 2008 by Dwayne C. Litzenberger <dlitz@dlitz.net>
|
||||
#
|
||||
# ===================================================================
|
||||
# The contents of this file are dedicated to the public domain. To
|
||||
# the extent that dedication to the public domain is not available,
|
||||
# everyone is granted a worldwide, perpetual, royalty-free,
|
||||
# non-exclusive license to exercise all rights associated with the
|
||||
# contents of this file for any purpose whatsoever.
|
||||
# No rights are reserved.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
# SOFTWARE.
|
||||
# ===================================================================
|
||||
|
||||
__revision__ = "$Id$"
|
||||
|
||||
import sys
|
||||
if sys.version_info[0] == 2 and sys.version_info[1] == 1:
|
||||
from Crypto.Util.py21compat import *
|
||||
|
||||
import os
|
||||
import threading
|
||||
import struct
|
||||
import time
|
||||
from math import floor
|
||||
|
||||
from Crypto.Random import OSRNG
|
||||
from Crypto.Random.Fortuna import FortunaAccumulator
|
||||
|
||||
class _EntropySource(object):
|
||||
def __init__(self, accumulator, src_num):
|
||||
self._fortuna = accumulator
|
||||
self._src_num = src_num
|
||||
self._pool_num = 0
|
||||
|
||||
def feed(self, data):
|
||||
self._fortuna.add_random_event(self._src_num, self._pool_num, data)
|
||||
self._pool_num = (self._pool_num + 1) & 31
|
||||
|
||||
class _EntropyCollector(object):
|
||||
|
||||
def __init__(self, accumulator):
|
||||
self._osrng = OSRNG.new()
|
||||
self._osrng_es = _EntropySource(accumulator, 255)
|
||||
self._time_es = _EntropySource(accumulator, 254)
|
||||
self._clock_es = _EntropySource(accumulator, 253)
|
||||
|
||||
def reinit(self):
|
||||
# Add 256 bits to each of the 32 pools, twice. (For a total of 16384
|
||||
# bits collected from the operating system.)
|
||||
for i in range(2):
|
||||
block = self._osrng.read(32*32)
|
||||
for p in range(32):
|
||||
self._osrng_es.feed(block[p*32:(p+1)*32])
|
||||
block = None
|
||||
self._osrng.flush()
|
||||
|
||||
def collect(self):
|
||||
# Collect 64 bits of entropy from the operating system and feed it to Fortuna.
|
||||
self._osrng_es.feed(self._osrng.read(8))
|
||||
|
||||
# Add the fractional part of time.time()
|
||||
t = time.time()
|
||||
self._time_es.feed(struct.pack("@I", int(2**30 * (t - floor(t)))))
|
||||
|
||||
# Add the fractional part of time.clock()
|
||||
t = time.clock()
|
||||
self._clock_es.feed(struct.pack("@I", int(2**30 * (t - floor(t)))))
|
||||
|
||||
|
||||
class _UserFriendlyRNG(object):
|
||||
|
||||
def __init__(self):
|
||||
self.closed = False
|
||||
self._fa = FortunaAccumulator.FortunaAccumulator()
|
||||
self._ec = _EntropyCollector(self._fa)
|
||||
self.reinit()
|
||||
|
||||
def reinit(self):
|
||||
"""Initialize the random number generator and seed it with entropy from
|
||||
the operating system.
|
||||
"""
|
||||
|
||||
# Save the pid (helps ensure that Crypto.Random.atfork() gets called)
|
||||
self._pid = os.getpid()
|
||||
|
||||
# Collect entropy from the operating system and feed it to
|
||||
# FortunaAccumulator
|
||||
self._ec.reinit()
|
||||
|
||||
# Override FortunaAccumulator's 100ms minimum re-seed interval. This
|
||||
# is necessary to avoid a race condition between this function and
|
||||
# self.read(), which that can otherwise cause forked child processes to
|
||||
# produce identical output. (e.g. CVE-2013-1445)
|
||||
#
|
||||
# Note that if this function can be called frequently by an attacker,
|
||||
# (and if the bits from OSRNG are insufficiently random) it will weaken
|
||||
# Fortuna's ability to resist a state compromise extension attack.
|
||||
self._fa._forget_last_reseed()
|
||||
|
||||
def close(self):
|
||||
self.closed = True
|
||||
self._osrng = None
|
||||
self._fa = None
|
||||
|
||||
def flush(self):
|
||||
pass
|
||||
|
||||
def read(self, N):
|
||||
"""Return N bytes from the RNG."""
|
||||
if self.closed:
|
||||
raise ValueError("I/O operation on closed file")
|
||||
if not isinstance(N, (long, int)):
|
||||
raise TypeError("an integer is required")
|
||||
if N < 0:
|
||||
raise ValueError("cannot read to end of infinite stream")
|
||||
|
||||
# Collect some entropy and feed it to Fortuna
|
||||
self._ec.collect()
|
||||
|
||||
# Ask Fortuna to generate some bytes
|
||||
retval = self._fa.random_data(N)
|
||||
|
||||
# Check that we haven't forked in the meantime. (If we have, we don't
|
||||
# want to use the data, because it might have been duplicated in the
|
||||
# parent process.
|
||||
self._check_pid()
|
||||
|
||||
# Return the random data.
|
||||
return retval
|
||||
|
||||
def _check_pid(self):
|
||||
# Lame fork detection to remind developers to invoke Random.atfork()
|
||||
# after every call to os.fork(). Note that this check is not reliable,
|
||||
# since process IDs can be reused on most operating systems.
|
||||
#
|
||||
# You need to do Random.atfork() in the child process after every call
|
||||
# to os.fork() to avoid reusing PRNG state. If you want to avoid
|
||||
# leaking PRNG state to child processes (for example, if you are using
|
||||
# os.setuid()) then you should also invoke Random.atfork() in the
|
||||
# *parent* process.
|
||||
if os.getpid() != self._pid:
|
||||
raise AssertionError("PID check failed. RNG must be re-initialized after fork(). Hint: Try Random.atfork()")
|
||||
|
||||
|
||||
class _LockingUserFriendlyRNG(_UserFriendlyRNG):
|
||||
def __init__(self):
|
||||
self._lock = threading.Lock()
|
||||
_UserFriendlyRNG.__init__(self)
|
||||
|
||||
def close(self):
|
||||
self._lock.acquire()
|
||||
try:
|
||||
return _UserFriendlyRNG.close(self)
|
||||
finally:
|
||||
self._lock.release()
|
||||
|
||||
def reinit(self):
|
||||
self._lock.acquire()
|
||||
try:
|
||||
return _UserFriendlyRNG.reinit(self)
|
||||
finally:
|
||||
self._lock.release()
|
||||
|
||||
def read(self, bytes):
|
||||
self._lock.acquire()
|
||||
try:
|
||||
return _UserFriendlyRNG.read(self, bytes)
|
||||
finally:
|
||||
self._lock.release()
|
||||
|
||||
class RNGFile(object):
|
||||
def __init__(self, singleton):
|
||||
self.closed = False
|
||||
self._singleton = singleton
|
||||
|
||||
# PEP 343: Support for the "with" statement
|
||||
def __enter__(self):
|
||||
"""PEP 343 support"""
|
||||
def __exit__(self):
|
||||
"""PEP 343 support"""
|
||||
self.close()
|
||||
|
||||
def close(self):
|
||||
# Don't actually close the singleton, just close this RNGFile instance.
|
||||
self.closed = True
|
||||
self._singleton = None
|
||||
|
||||
def read(self, bytes):
|
||||
if self.closed:
|
||||
raise ValueError("I/O operation on closed file")
|
||||
return self._singleton.read(bytes)
|
||||
|
||||
def flush(self):
|
||||
if self.closed:
|
||||
raise ValueError("I/O operation on closed file")
|
||||
|
||||
_singleton_lock = threading.Lock()
|
||||
_singleton = None
|
||||
def _get_singleton():
|
||||
global _singleton
|
||||
_singleton_lock.acquire()
|
||||
try:
|
||||
if _singleton is None:
|
||||
_singleton = _LockingUserFriendlyRNG()
|
||||
return _singleton
|
||||
finally:
|
||||
_singleton_lock.release()
|
||||
|
||||
def new():
|
||||
return RNGFile(_get_singleton())
|
||||
|
||||
def reinit():
|
||||
_get_singleton().reinit()
|
||||
|
||||
def get_random_bytes(n):
|
||||
"""Return the specified number of cryptographically-strong random bytes."""
|
||||
return _get_singleton().read(n)
|
||||
|
||||
# vim:set ts=4 sw=4 sts=4 expandtab:
|
43
modules/Crypto/Random/__init__.py
Normal file
43
modules/Crypto/Random/__init__.py
Normal file
@ -0,0 +1,43 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Random/__init__.py : PyCrypto random number generation
|
||||
#
|
||||
# Written in 2008 by Dwayne C. Litzenberger <dlitz@dlitz.net>
|
||||
#
|
||||
# ===================================================================
|
||||
# The contents of this file are dedicated to the public domain. To
|
||||
# the extent that dedication to the public domain is not available,
|
||||
# everyone is granted a worldwide, perpetual, royalty-free,
|
||||
# non-exclusive license to exercise all rights associated with the
|
||||
# contents of this file for any purpose whatsoever.
|
||||
# No rights are reserved.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
# SOFTWARE.
|
||||
# ===================================================================
|
||||
|
||||
__revision__ = "$Id$"
|
||||
__all__ = ['new']
|
||||
|
||||
from Crypto.Random import OSRNG
|
||||
from Crypto.Random import _UserFriendlyRNG
|
||||
|
||||
def new(*args, **kwargs):
|
||||
"""Return a file-like object that outputs cryptographically random bytes."""
|
||||
return _UserFriendlyRNG.new(*args, **kwargs)
|
||||
|
||||
def atfork():
|
||||
"""Call this whenever you call os.fork()"""
|
||||
_UserFriendlyRNG.reinit()
|
||||
|
||||
def get_random_bytes(n):
|
||||
"""Return the specified number of cryptographically-strong random bytes."""
|
||||
return _UserFriendlyRNG.get_random_bytes(n)
|
||||
|
||||
# vim:set ts=4 sw=4 sts=4 expandtab:
|
142
modules/Crypto/Random/random.py
Normal file
142
modules/Crypto/Random/random.py
Normal file
@ -0,0 +1,142 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Random/random.py : Strong alternative for the standard 'random' module
|
||||
#
|
||||
# Written in 2008 by Dwayne C. Litzenberger <dlitz@dlitz.net>
|
||||
#
|
||||
# ===================================================================
|
||||
# The contents of this file are dedicated to the public domain. To
|
||||
# the extent that dedication to the public domain is not available,
|
||||
# everyone is granted a worldwide, perpetual, royalty-free,
|
||||
# non-exclusive license to exercise all rights associated with the
|
||||
# contents of this file for any purpose whatsoever.
|
||||
# No rights are reserved.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
# SOFTWARE.
|
||||
# ===================================================================
|
||||
|
||||
"""A cryptographically strong version of Python's standard "random" module."""
|
||||
|
||||
__revision__ = "$Id$"
|
||||
__all__ = ['StrongRandom', 'getrandbits', 'randrange', 'randint', 'choice', 'shuffle', 'sample']
|
||||
|
||||
from Crypto import Random
|
||||
import sys
|
||||
if sys.version_info[0] == 2 and sys.version_info[1] == 1:
|
||||
from Crypto.Util.py21compat import *
|
||||
|
||||
class StrongRandom(object):
|
||||
def __init__(self, rng=None, randfunc=None):
|
||||
if randfunc is None and rng is None:
|
||||
self._randfunc = None
|
||||
elif randfunc is not None and rng is None:
|
||||
self._randfunc = randfunc
|
||||
elif randfunc is None and rng is not None:
|
||||
self._randfunc = rng.read
|
||||
else:
|
||||
raise ValueError("Cannot specify both 'rng' and 'randfunc'")
|
||||
|
||||
def getrandbits(self, k):
|
||||
"""Return a python long integer with k random bits."""
|
||||
if self._randfunc is None:
|
||||
self._randfunc = Random.new().read
|
||||
mask = (1L << k) - 1
|
||||
return mask & bytes_to_long(self._randfunc(ceil_div(k, 8)))
|
||||
|
||||
def randrange(self, *args):
|
||||
"""randrange([start,] stop[, step]):
|
||||
Return a randomly-selected element from range(start, stop, step)."""
|
||||
if len(args) == 3:
|
||||
(start, stop, step) = args
|
||||
elif len(args) == 2:
|
||||
(start, stop) = args
|
||||
step = 1
|
||||
elif len(args) == 1:
|
||||
(stop,) = args
|
||||
start = 0
|
||||
step = 1
|
||||
else:
|
||||
raise TypeError("randrange expected at most 3 arguments, got %d" % (len(args),))
|
||||
if (not isinstance(start, (int, long))
|
||||
or not isinstance(stop, (int, long))
|
||||
or not isinstance(step, (int, long))):
|
||||
raise TypeError("randrange requires integer arguments")
|
||||
if step == 0:
|
||||
raise ValueError("randrange step argument must not be zero")
|
||||
|
||||
num_choices = ceil_div(stop - start, step)
|
||||
if num_choices < 0:
|
||||
num_choices = 0
|
||||
if num_choices < 1:
|
||||
raise ValueError("empty range for randrange(%r, %r, %r)" % (start, stop, step))
|
||||
|
||||
# Pick a random number in the range of possible numbers
|
||||
r = num_choices
|
||||
while r >= num_choices:
|
||||
r = self.getrandbits(size(num_choices))
|
||||
|
||||
return start + (step * r)
|
||||
|
||||
def randint(self, a, b):
|
||||
"""Return a random integer N such that a <= N <= b."""
|
||||
if not isinstance(a, (int, long)) or not isinstance(b, (int, long)):
|
||||
raise TypeError("randint requires integer arguments")
|
||||
N = self.randrange(a, b+1)
|
||||
assert a <= N <= b
|
||||
return N
|
||||
|
||||
def choice(self, seq):
|
||||
"""Return a random element from a (non-empty) sequence.
|
||||
|
||||
If the seqence is empty, raises IndexError.
|
||||
"""
|
||||
if len(seq) == 0:
|
||||
raise IndexError("empty sequence")
|
||||
return seq[self.randrange(len(seq))]
|
||||
|
||||
def shuffle(self, x):
|
||||
"""Shuffle the sequence in place."""
|
||||
# Fisher-Yates shuffle. O(n)
|
||||
# See http://en.wikipedia.org/wiki/Fisher-Yates_shuffle
|
||||
# Working backwards from the end of the array, we choose a random item
|
||||
# from the remaining items until all items have been chosen.
|
||||
for i in xrange(len(x)-1, 0, -1): # iterate from len(x)-1 downto 1
|
||||
j = self.randrange(0, i+1) # choose random j such that 0 <= j <= i
|
||||
x[i], x[j] = x[j], x[i] # exchange x[i] and x[j]
|
||||
|
||||
def sample(self, population, k):
|
||||
"""Return a k-length list of unique elements chosen from the population sequence."""
|
||||
|
||||
num_choices = len(population)
|
||||
if k > num_choices:
|
||||
raise ValueError("sample larger than population")
|
||||
|
||||
retval = []
|
||||
selected = {} # we emulate a set using a dict here
|
||||
for i in xrange(k):
|
||||
r = None
|
||||
while r is None or selected.has_key(r):
|
||||
r = self.randrange(num_choices)
|
||||
retval.append(population[r])
|
||||
selected[r] = 1
|
||||
return retval
|
||||
|
||||
_r = StrongRandom()
|
||||
getrandbits = _r.getrandbits
|
||||
randrange = _r.randrange
|
||||
randint = _r.randint
|
||||
choice = _r.choice
|
||||
shuffle = _r.shuffle
|
||||
sample = _r.sample
|
||||
|
||||
# These are at the bottom to avoid problems with recursive imports
|
||||
from Crypto.Util.number import ceil_div, bytes_to_long, long_to_bytes, size
|
||||
|
||||
# vim:set ts=4 sw=4 sts=4 expandtab:
|
48
modules/Crypto/SelfTest/Cipher/__init__.py
Normal file
48
modules/Crypto/SelfTest/Cipher/__init__.py
Normal file
@ -0,0 +1,48 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# SelfTest/Cipher/__init__.py: Self-test for cipher modules
|
||||
#
|
||||
# Written in 2008 by Dwayne C. Litzenberger <dlitz@dlitz.net>
|
||||
#
|
||||
# ===================================================================
|
||||
# The contents of this file are dedicated to the public domain. To
|
||||
# the extent that dedication to the public domain is not available,
|
||||
# everyone is granted a worldwide, perpetual, royalty-free,
|
||||
# non-exclusive license to exercise all rights associated with the
|
||||
# contents of this file for any purpose whatsoever.
|
||||
# No rights are reserved.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
# SOFTWARE.
|
||||
# ===================================================================
|
||||
|
||||
"""Self-test for cipher modules"""
|
||||
|
||||
__revision__ = "$Id$"
|
||||
|
||||
def get_tests(config={}):
|
||||
tests = []
|
||||
from Crypto.SelfTest.Cipher import test_AES; tests += test_AES.get_tests(config=config)
|
||||
from Crypto.SelfTest.Cipher import test_ARC2; tests += test_ARC2.get_tests(config=config)
|
||||
from Crypto.SelfTest.Cipher import test_ARC4; tests += test_ARC4.get_tests(config=config)
|
||||
from Crypto.SelfTest.Cipher import test_Blowfish; tests += test_Blowfish.get_tests(config=config)
|
||||
from Crypto.SelfTest.Cipher import test_CAST; tests += test_CAST.get_tests(config=config)
|
||||
from Crypto.SelfTest.Cipher import test_DES3; tests += test_DES3.get_tests(config=config)
|
||||
from Crypto.SelfTest.Cipher import test_DES; tests += test_DES.get_tests(config=config)
|
||||
from Crypto.SelfTest.Cipher import test_XOR; tests += test_XOR.get_tests(config=config)
|
||||
from Crypto.SelfTest.Cipher import test_pkcs1_15; tests += test_pkcs1_15.get_tests(config=config)
|
||||
from Crypto.SelfTest.Cipher import test_pkcs1_oaep; tests += test_pkcs1_oaep.get_tests(config=config)
|
||||
return tests
|
||||
|
||||
if __name__ == '__main__':
|
||||
import unittest
|
||||
suite = lambda: unittest.TestSuite(get_tests())
|
||||
unittest.main(defaultTest='suite')
|
||||
|
||||
# vim:set ts=4 sw=4 sts=4 expandtab:
|
796
modules/Crypto/SelfTest/Cipher/common.py
Normal file
796
modules/Crypto/SelfTest/Cipher/common.py
Normal file
@ -0,0 +1,796 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# SelfTest/Hash/common.py: Common code for Crypto.SelfTest.Hash
|
||||
#
|
||||
# Written in 2008 by Dwayne C. Litzenberger <dlitz@dlitz.net>
|
||||
#
|
||||
# ===================================================================
|
||||
# The contents of this file are dedicated to the public domain. To
|
||||
# the extent that dedication to the public domain is not available,
|
||||
# everyone is granted a worldwide, perpetual, royalty-free,
|
||||
# non-exclusive license to exercise all rights associated with the
|
||||
# contents of this file for any purpose whatsoever.
|
||||
# No rights are reserved.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
# SOFTWARE.
|
||||
# ===================================================================
|
||||
|
||||
"""Self-testing for PyCrypto hash modules"""
|
||||
|
||||
from __future__ import nested_scopes
|
||||
|
||||
__revision__ = "$Id$"
|
||||
|
||||
import sys
|
||||
if sys.version_info[0] == 2 and sys.version_info[1] == 1:
|
||||
from Crypto.Util.py21compat import *
|
||||
|
||||
import unittest
|
||||
from binascii import a2b_hex, b2a_hex, hexlify
|
||||
|
||||
from Crypto.Util.py3compat import *
|
||||
from Crypto.Util.strxor import strxor_c
|
||||
|
||||
# For compatibility with Python 2.1 and Python 2.2
|
||||
if sys.hexversion < 0x02030000:
|
||||
# Python 2.1 doesn't have a dict() function
|
||||
# Python 2.2 dict() function raises TypeError if you do dict(MD5='blah')
|
||||
def dict(**kwargs):
|
||||
return kwargs.copy()
|
||||
else:
|
||||
dict = dict
|
||||
|
||||
class _NoDefault: pass # sentinel object
|
||||
def _extract(d, k, default=_NoDefault):
|
||||
"""Get an item from a dictionary, and remove it from the dictionary."""
|
||||
try:
|
||||
retval = d[k]
|
||||
except KeyError:
|
||||
if default is _NoDefault:
|
||||
raise
|
||||
return default
|
||||
del d[k]
|
||||
return retval
|
||||
|
||||
# Generic cipher test case
|
||||
class CipherSelfTest(unittest.TestCase):
|
||||
|
||||
def __init__(self, module, params):
|
||||
unittest.TestCase.__init__(self)
|
||||
self.module = module
|
||||
|
||||
# Extract the parameters
|
||||
params = params.copy()
|
||||
self.description = _extract(params, 'description')
|
||||
self.key = b(_extract(params, 'key'))
|
||||
self.plaintext = b(_extract(params, 'plaintext'))
|
||||
self.ciphertext = b(_extract(params, 'ciphertext'))
|
||||
self.module_name = _extract(params, 'module_name', None)
|
||||
self.assoc_data = _extract(params, 'assoc_data', None)
|
||||
self.mac = _extract(params, 'mac', None)
|
||||
if self.assoc_data:
|
||||
self.mac = b(self.mac)
|
||||
|
||||
mode = _extract(params, 'mode', None)
|
||||
self.mode_name = str(mode)
|
||||
if mode is not None:
|
||||
# Block cipher
|
||||
self.mode = getattr(self.module, "MODE_" + mode)
|
||||
|
||||
self.iv = _extract(params, 'iv', None)
|
||||
if self.iv is None:
|
||||
self.iv = _extract(params, 'nonce', None)
|
||||
if self.iv is not None:
|
||||
self.iv = b(self.iv)
|
||||
|
||||
# Only relevant for OPENPGP mode
|
||||
self.encrypted_iv = _extract(params, 'encrypted_iv', None)
|
||||
if self.encrypted_iv is not None:
|
||||
self.encrypted_iv = b(self.encrypted_iv)
|
||||
else:
|
||||
# Stream cipher
|
||||
self.mode = None
|
||||
self.iv = None
|
||||
|
||||
self.extra_params = params
|
||||
|
||||
def shortDescription(self):
|
||||
return self.description
|
||||
|
||||
def _new(self, do_decryption=0):
|
||||
params = self.extra_params.copy()
|
||||
|
||||
# Handle CTR mode parameters. By default, we use Counter.new(self.module.block_size)
|
||||
if hasattr(self.module, "MODE_CTR") and self.mode == self.module.MODE_CTR:
|
||||
from Crypto.Util import Counter
|
||||
ctr_class = _extract(params, 'ctr_class', Counter.new)
|
||||
ctr_params = _extract(params, 'ctr_params', {}).copy()
|
||||
if ctr_params.has_key('prefix'): ctr_params['prefix'] = a2b_hex(b(ctr_params['prefix']))
|
||||
if ctr_params.has_key('suffix'): ctr_params['suffix'] = a2b_hex(b(ctr_params['suffix']))
|
||||
if not ctr_params.has_key('nbits'):
|
||||
ctr_params['nbits'] = 8*(self.module.block_size - len(ctr_params.get('prefix', '')) - len(ctr_params.get('suffix', '')))
|
||||
params['counter'] = ctr_class(**ctr_params)
|
||||
|
||||
if self.mode is None:
|
||||
# Stream cipher
|
||||
return self.module.new(a2b_hex(self.key), **params)
|
||||
elif self.iv is None:
|
||||
# Block cipher without iv
|
||||
return self.module.new(a2b_hex(self.key), self.mode, **params)
|
||||
else:
|
||||
# Block cipher with iv
|
||||
if do_decryption and self.mode == self.module.MODE_OPENPGP:
|
||||
# In PGP mode, the IV to feed for decryption is the *encrypted* one
|
||||
return self.module.new(a2b_hex(self.key), self.mode, a2b_hex(self.encrypted_iv), **params)
|
||||
else:
|
||||
return self.module.new(a2b_hex(self.key), self.mode, a2b_hex(self.iv), **params)
|
||||
|
||||
def isMode(self, name):
|
||||
if not hasattr(self.module, "MODE_"+name):
|
||||
return False
|
||||
return self.mode == getattr(self.module, "MODE_"+name)
|
||||
|
||||
def runTest(self):
|
||||
plaintext = a2b_hex(self.plaintext)
|
||||
ciphertext = a2b_hex(self.ciphertext)
|
||||
assoc_data = []
|
||||
if self.assoc_data:
|
||||
assoc_data = [ a2b_hex(b(x)) for x in self.assoc_data]
|
||||
|
||||
ct = None
|
||||
pt = None
|
||||
|
||||
#
|
||||
# Repeat the same encryption or decryption twice and verify
|
||||
# that the result is always the same
|
||||
#
|
||||
for i in xrange(2):
|
||||
cipher = self._new()
|
||||
decipher = self._new(1)
|
||||
|
||||
# Only AEAD modes
|
||||
for comp in assoc_data:
|
||||
cipher.update(comp)
|
||||
decipher.update(comp)
|
||||
|
||||
ctX = b2a_hex(cipher.encrypt(plaintext))
|
||||
if self.isMode("SIV"):
|
||||
ptX = b2a_hex(decipher.decrypt_and_verify(ciphertext, a2b_hex(self.mac)))
|
||||
else:
|
||||
ptX = b2a_hex(decipher.decrypt(ciphertext))
|
||||
|
||||
if ct:
|
||||
self.assertEqual(ct, ctX)
|
||||
self.assertEqual(pt, ptX)
|
||||
ct, pt = ctX, ptX
|
||||
|
||||
if self.isMode("OPENPGP"):
|
||||
# In PGP mode, data returned by the first encrypt()
|
||||
# is prefixed with the encrypted IV.
|
||||
# Here we check it and then remove it from the ciphertexts.
|
||||
eilen = len(self.encrypted_iv)
|
||||
self.assertEqual(self.encrypted_iv, ct[:eilen])
|
||||
ct = ct[eilen:]
|
||||
|
||||
self.assertEqual(self.ciphertext, ct) # encrypt
|
||||
self.assertEqual(self.plaintext, pt) # decrypt
|
||||
|
||||
if self.mac:
|
||||
mac = b2a_hex(cipher.digest())
|
||||
self.assertEqual(self.mac, mac)
|
||||
decipher.verify(a2b_hex(self.mac))
|
||||
|
||||
class CipherStreamingSelfTest(CipherSelfTest):
|
||||
|
||||
def shortDescription(self):
|
||||
desc = self.module_name
|
||||
if self.mode is not None:
|
||||
desc += " in %s mode" % (self.mode_name,)
|
||||
return "%s should behave like a stream cipher" % (desc,)
|
||||
|
||||
def runTest(self):
|
||||
plaintext = a2b_hex(self.plaintext)
|
||||
ciphertext = a2b_hex(self.ciphertext)
|
||||
|
||||
# The cipher should work like a stream cipher
|
||||
|
||||
# Test counter mode encryption, 3 bytes at a time
|
||||
ct3 = []
|
||||
cipher = self._new()
|
||||
for i in range(0, len(plaintext), 3):
|
||||
ct3.append(cipher.encrypt(plaintext[i:i+3]))
|
||||
ct3 = b2a_hex(b("").join(ct3))
|
||||
self.assertEqual(self.ciphertext, ct3) # encryption (3 bytes at a time)
|
||||
|
||||
# Test counter mode decryption, 3 bytes at a time
|
||||
pt3 = []
|
||||
cipher = self._new()
|
||||
for i in range(0, len(ciphertext), 3):
|
||||
pt3.append(cipher.encrypt(ciphertext[i:i+3]))
|
||||
# PY3K: This is meant to be text, do not change to bytes (data)
|
||||
pt3 = b2a_hex(b("").join(pt3))
|
||||
self.assertEqual(self.plaintext, pt3) # decryption (3 bytes at a time)
|
||||
|
||||
class CTRSegfaultTest(unittest.TestCase):
|
||||
|
||||
def __init__(self, module, params):
|
||||
unittest.TestCase.__init__(self)
|
||||
self.module = module
|
||||
self.key = b(params['key'])
|
||||
self.module_name = params.get('module_name', None)
|
||||
|
||||
def shortDescription(self):
|
||||
return """Regression test: %s.new(key, %s.MODE_CTR) should raise TypeError, not segfault""" % (self.module_name, self.module_name)
|
||||
|
||||
def runTest(self):
|
||||
self.assertRaises(TypeError, self.module.new, a2b_hex(self.key), self.module.MODE_CTR)
|
||||
|
||||
class CTRWraparoundTest(unittest.TestCase):
|
||||
|
||||
def __init__(self, module, params):
|
||||
unittest.TestCase.__init__(self)
|
||||
self.module = module
|
||||
self.key = b(params['key'])
|
||||
self.module_name = params.get('module_name', None)
|
||||
|
||||
def shortDescription(self):
|
||||
return """Regression test: %s with MODE_CTR raising OverflowError on wraparound""" % (self.module_name,)
|
||||
|
||||
def runTest(self):
|
||||
from Crypto.Util import Counter
|
||||
|
||||
def pythonCounter():
|
||||
state = [0]
|
||||
def ctr():
|
||||
# First block succeeds; Second and subsequent blocks raise OverflowError
|
||||
if state[0] == 0:
|
||||
state[0] = 1
|
||||
return b("\xff") * self.module.block_size
|
||||
else:
|
||||
raise OverflowError
|
||||
return ctr
|
||||
|
||||
for little_endian in (0, 1): # (False, True) Test both endiannesses
|
||||
block = b("\x00") * self.module.block_size
|
||||
|
||||
# Test PyObject_CallObject code path: if the counter raises OverflowError
|
||||
cipher = self.module.new(a2b_hex(self.key), self.module.MODE_CTR, counter=pythonCounter())
|
||||
cipher.encrypt(block)
|
||||
self.assertRaises(OverflowError, cipher.encrypt, block)
|
||||
self.assertRaises(OverflowError, cipher.encrypt, block)
|
||||
|
||||
# Test PyObject_CallObject code path: counter object should raise OverflowError
|
||||
ctr = Counter.new(8*self.module.block_size, initial_value=2L**(8*self.module.block_size)-1, little_endian=little_endian)
|
||||
ctr()
|
||||
self.assertRaises(OverflowError, ctr)
|
||||
self.assertRaises(OverflowError, ctr)
|
||||
|
||||
# Test the CTR-mode shortcut
|
||||
ctr = Counter.new(8*self.module.block_size, initial_value=2L**(8*self.module.block_size)-1, little_endian=little_endian)
|
||||
cipher = self.module.new(a2b_hex(self.key), self.module.MODE_CTR, counter=ctr)
|
||||
cipher.encrypt(block)
|
||||
self.assertRaises(OverflowError, cipher.encrypt, block)
|
||||
self.assertRaises(OverflowError, cipher.encrypt, block)
|
||||
|
||||
class CFBSegmentSizeTest(unittest.TestCase):
|
||||
|
||||
def __init__(self, module, params):
|
||||
unittest.TestCase.__init__(self)
|
||||
self.module = module
|
||||
self.key = b(params['key'])
|
||||
self.description = params['description']
|
||||
|
||||
def shortDescription(self):
|
||||
return self.description
|
||||
|
||||
def runTest(self):
|
||||
"""Regression test: m.new(key, m.MODE_CFB, segment_size=N) should require segment_size to be a multiple of 8 bits"""
|
||||
for i in range(1, 8):
|
||||
self.assertRaises(ValueError, self.module.new, a2b_hex(self.key), self.module.MODE_CFB, segment_size=i)
|
||||
self.module.new(a2b_hex(self.key), self.module.MODE_CFB, "\0"*self.module.block_size, segment_size=8) # should succeed
|
||||
|
||||
class CCMMACLengthTest(unittest.TestCase):
|
||||
"""CCM specific tests about MAC"""
|
||||
|
||||
def __init__(self, module):
|
||||
unittest.TestCase.__init__(self)
|
||||
self.module = module
|
||||
self.key = b('\xFF')*16
|
||||
self.iv = b('\x00')*10
|
||||
|
||||
def shortDescription(self):
|
||||
return self.description
|
||||
|
||||
def runTest(self):
|
||||
"""Verify that MAC can only be 4,6,8,..,16 bytes long."""
|
||||
for i in range(3,16,2):
|
||||
self.description = "CCM MAC length check (%d bytes)" % i
|
||||
self.assertRaises(ValueError, self.module.new, self.key,
|
||||
self.module.MODE_CCM, self.iv, msg_len=10, mac_len=i)
|
||||
|
||||
"""Verify that default MAC length is 16."""
|
||||
self.description = "CCM default MAC length check"
|
||||
cipher = self.module.new(self.key, self.module.MODE_CCM,
|
||||
self.iv, msg_len=4)
|
||||
cipher.encrypt(b('z')*4)
|
||||
self.assertEqual(len(cipher.digest()), 16)
|
||||
|
||||
class CCMSplitEncryptionTest(unittest.TestCase):
|
||||
"""CCM specific tests to validate how encrypt()
|
||||
decrypt() can be called multiple times on the
|
||||
same object."""
|
||||
|
||||
def __init__(self, module):
|
||||
unittest.TestCase.__init__(self)
|
||||
self.module = module
|
||||
self.key = b('\xFF')*16
|
||||
self.iv = b('\x00')*10
|
||||
self.description = "CCM Split Encryption Test"
|
||||
|
||||
def shortDescription(self):
|
||||
return self.description
|
||||
|
||||
def runTest(self):
|
||||
"""Verify that CCM update()/encrypt() can be called multiple times,
|
||||
provided that lengths are declared beforehand"""
|
||||
|
||||
data = b("AUTH DATA")
|
||||
pt1 = b("PLAINTEXT1") # Short
|
||||
pt2 = b("PLAINTEXT2") # Long
|
||||
pt_ref = pt1+pt2
|
||||
|
||||
# REFERENCE: Run with 1 update() and 1 encrypt()
|
||||
cipher = self.module.new(self.key, self.module.MODE_CCM,
|
||||
self.iv)
|
||||
cipher.update(data)
|
||||
ct_ref = cipher.encrypt(pt_ref)
|
||||
mac_ref = cipher.digest()
|
||||
|
||||
# Verify that calling CCM encrypt()/decrypt() twice is not
|
||||
# possible without the 'msg_len' parameter and regardless
|
||||
# of the 'assoc_len' parameter
|
||||
for ad_len in None, len(data):
|
||||
cipher = self.module.new(self.key, self.module.MODE_CCM,
|
||||
self.iv, assoc_len=ad_len)
|
||||
cipher.update(data)
|
||||
cipher.encrypt(pt1)
|
||||
self.assertRaises(TypeError, cipher.encrypt, pt2)
|
||||
|
||||
cipher = self.module.new(self.key, self.module.MODE_CCM,
|
||||
self.iv, assoc_len=ad_len)
|
||||
cipher.update(data)
|
||||
cipher.decrypt(ct_ref[:len(pt1)])
|
||||
self.assertRaises(TypeError, cipher.decrypt, ct_ref[len(pt1):])
|
||||
|
||||
# Run with 2 encrypt()/decrypt(). Results must be the same
|
||||
# regardless of the 'assoc_len' parameter
|
||||
for ad_len in None, len(data):
|
||||
cipher = self.module.new(self.key, self.module.MODE_CCM,
|
||||
self.iv, assoc_len=ad_len, msg_len=len(pt_ref))
|
||||
cipher.update(data)
|
||||
ct = cipher.encrypt(pt1)
|
||||
ct += cipher.encrypt(pt2)
|
||||
mac = cipher.digest()
|
||||
self.assertEqual(ct_ref, ct)
|
||||
self.assertEqual(mac_ref, mac)
|
||||
|
||||
cipher = self.module.new(self.key, self.module.MODE_CCM,
|
||||
self.iv, msg_len=len(pt1+pt2))
|
||||
cipher.update(data)
|
||||
pt = cipher.decrypt(ct[:len(pt1)])
|
||||
pt += cipher.decrypt(ct[len(pt1):])
|
||||
mac = cipher.verify(mac_ref)
|
||||
self.assertEqual(pt_ref, pt)
|
||||
|
||||
class AEADTests(unittest.TestCase):
|
||||
"""Tests generic to all AEAD modes"""
|
||||
|
||||
def __init__(self, module, mode_name, key_size):
|
||||
unittest.TestCase.__init__(self)
|
||||
self.module = module
|
||||
self.mode_name = mode_name
|
||||
self.mode = getattr(module, mode_name)
|
||||
if not self.isMode("SIV"):
|
||||
self.key = b('\xFF')*key_size
|
||||
else:
|
||||
self.key = b('\xFF')*key_size*2
|
||||
self.iv = b('\x00')*10
|
||||
self.description = "AEAD Test"
|
||||
|
||||
def isMode(self, name):
|
||||
if not hasattr(self.module, "MODE_"+name):
|
||||
return False
|
||||
return self.mode == getattr(self.module, "MODE_"+name)
|
||||
|
||||
def right_mac_test(self):
|
||||
"""Positive tests for MAC"""
|
||||
|
||||
self.description = "Test for right MAC in %s of %s" % \
|
||||
(self.mode_name, self.module.__name__)
|
||||
|
||||
ad_ref = b("Reference AD")
|
||||
pt_ref = b("Reference plaintext")
|
||||
|
||||
# Encrypt and create the reference MAC
|
||||
cipher = self.module.new(self.key, self.mode, self.iv)
|
||||
cipher.update(ad_ref)
|
||||
ct_ref = cipher.encrypt(pt_ref)
|
||||
mac_ref = cipher.digest()
|
||||
|
||||
# Decrypt and verify that MAC is accepted
|
||||
decipher = self.module.new(self.key, self.mode, self.iv)
|
||||
decipher.update(ad_ref)
|
||||
pt = decipher.decrypt_and_verify(ct_ref, mac_ref)
|
||||
self.assertEqual(pt, pt_ref)
|
||||
|
||||
# Verify that hexverify work
|
||||
decipher.hexverify(hexlify(mac_ref))
|
||||
|
||||
def wrong_mac_test(self):
|
||||
"""Negative tests for MAC"""
|
||||
|
||||
self.description = "Test for wrong MAC in %s of %s" % \
|
||||
(self.mode_name, self.module.__name__)
|
||||
|
||||
ad_ref = b("Reference AD")
|
||||
pt_ref = b("Reference plaintext")
|
||||
|
||||
# Encrypt and create the reference MAC
|
||||
cipher = self.module.new(self.key, self.mode, self.iv)
|
||||
cipher.update(ad_ref)
|
||||
ct_ref = cipher.encrypt(pt_ref)
|
||||
mac_ref = cipher.digest()
|
||||
|
||||
# Modify the MAC and verify it is NOT ACCEPTED
|
||||
wrong_mac = strxor_c(mac_ref, 255)
|
||||
decipher = self.module.new(self.key, self.mode, self.iv)
|
||||
decipher.update(ad_ref)
|
||||
self.assertRaises(ValueError, decipher.decrypt_and_verify,
|
||||
ct_ref, wrong_mac)
|
||||
|
||||
def zero_data(self):
|
||||
"""Verify transition from INITIALIZED to FINISHED"""
|
||||
|
||||
self.description = "Test for zero data in %s of %s" % \
|
||||
(self.mode_name, self.module.__name__)
|
||||
cipher = self.module.new(self.key, self.mode, self.iv)
|
||||
cipher.digest()
|
||||
|
||||
def multiple_updates(self):
|
||||
"""Verify that update() can be called multiple times"""
|
||||
|
||||
self.description = "Test for multiple updates in %s of %s" % \
|
||||
(self.mode_name, self.module.__name__)
|
||||
|
||||
# In all modes other than SIV, the associated data is a single
|
||||
# component that can be arbitrarilly split and submitted to update().
|
||||
#
|
||||
# In SIV, associated data is instead organized in a vector or multiple
|
||||
# components. Each component is passed to update() as a whole.
|
||||
# This test is therefore not meaningful to SIV.
|
||||
if self.isMode("SIV"):
|
||||
return
|
||||
|
||||
ad = b("").join([bchr(x) for x in xrange(0,128)])
|
||||
|
||||
mac1, mac2, mac3 = (None,)*3
|
||||
for chunk_length in 1,10,40,80,128:
|
||||
chunks = [ad[i:i+chunk_length] for i in range(0, len(ad), chunk_length)]
|
||||
|
||||
# No encryption/decryption
|
||||
cipher = self.module.new(self.key, self.mode, self.iv)
|
||||
for c in chunks:
|
||||
cipher.update(c)
|
||||
if mac1:
|
||||
cipher.verify(mac1)
|
||||
else:
|
||||
mac1 = cipher.digest()
|
||||
|
||||
# Encryption
|
||||
cipher = self.module.new(self.key, self.mode, self.iv)
|
||||
for c in chunks:
|
||||
cipher.update(c)
|
||||
ct = cipher.encrypt(b("PT"))
|
||||
mac2 = cipher.digest()
|
||||
|
||||
# Decryption
|
||||
cipher = self.module.new(self.key, self.mode, self.iv)
|
||||
for c in chunks:
|
||||
cipher.update(c)
|
||||
cipher.decrypt(ct)
|
||||
cipher.verify(mac2)
|
||||
|
||||
def no_mix_encrypt_decrypt(self):
|
||||
"""Verify that encrypt and decrypt cannot be mixed up"""
|
||||
|
||||
self.description = "Test for mix of encrypt and decrypt in %s of %s" % \
|
||||
(self.mode_name, self.module.__name__)
|
||||
|
||||
# Calling decrypt after encrypt raises an exception
|
||||
cipher = self.module.new(self.key, self.mode, self.iv)
|
||||
cipher.encrypt(b("PT")*40)
|
||||
self.assertRaises(TypeError, cipher.decrypt, b("XYZ")*40)
|
||||
|
||||
# Calling encrypt() after decrypt() raises an exception
|
||||
# (excluded for SIV, since decrypt() is not valid)
|
||||
if not self.isMode("SIV"):
|
||||
cipher = self.module.new(self.key, self.mode, self.iv)
|
||||
cipher.decrypt(b("CT")*40)
|
||||
self.assertRaises(TypeError, cipher.encrypt, b("XYZ")*40)
|
||||
|
||||
# Calling verify after encrypt raises an exception
|
||||
cipher = self.module.new(self.key, self.mode, self.iv)
|
||||
cipher.encrypt(b("PT")*40)
|
||||
self.assertRaises(TypeError, cipher.verify, b("XYZ"))
|
||||
self.assertRaises(TypeError, cipher.hexverify, "12")
|
||||
|
||||
# Calling digest() after decrypt() raises an exception
|
||||
# (excluded for SIV, since decrypt() is not valid)
|
||||
if not self.isMode("SIV"):
|
||||
cipher = self.module.new(self.key, self.mode, self.iv)
|
||||
cipher.decrypt(b("CT")*40)
|
||||
self.assertRaises(TypeError, cipher.digest)
|
||||
self.assertRaises(TypeError, cipher.hexdigest)
|
||||
|
||||
def no_late_update(self):
|
||||
"""Verify that update cannot be called after encrypt or decrypt"""
|
||||
|
||||
self.description = "Test for late update in %s of %s" % \
|
||||
(self.mode_name, self.module.__name__)
|
||||
|
||||
# Calling update after encrypt raises an exception
|
||||
cipher = self.module.new(self.key, self.mode, self.iv)
|
||||
cipher.update(b("XX"))
|
||||
cipher.encrypt(b("PT")*40)
|
||||
self.assertRaises(TypeError, cipher.update, b("XYZ"))
|
||||
|
||||
# Calling update() after decrypt() raises an exception
|
||||
# (excluded for SIV, since decrypt() is not valid)
|
||||
if not self.isMode("SIV"):
|
||||
cipher = self.module.new(self.key, self.mode, self.iv)
|
||||
cipher.update(b("XX"))
|
||||
cipher.decrypt(b("CT")*40)
|
||||
self.assertRaises(TypeError, cipher.update, b("XYZ"))
|
||||
|
||||
def loopback(self):
|
||||
"""Verify composition of encrypt_and_digest() and decrypt_and_verify()
|
||||
is the identity function."""
|
||||
|
||||
self.description = "Lookback test decrypt_and_verify(encrypt_and_digest)"\
|
||||
"for %s in %s" % (self.mode_name,
|
||||
self.module.__name__)
|
||||
|
||||
enc_cipher = self.module.new(self.key, self.mode, self.iv)
|
||||
dec_cipher = self.module.new(self.key, self.mode, self.iv)
|
||||
|
||||
enc_cipher.update(b("XXX"))
|
||||
dec_cipher.update(b("XXX"))
|
||||
|
||||
plaintext = b("Reference") * 10
|
||||
ct, mac = enc_cipher.encrypt_and_digest(plaintext)
|
||||
pt = dec_cipher.decrypt_and_verify(ct, mac)
|
||||
|
||||
self.assertEqual(plaintext, pt)
|
||||
|
||||
def runTest(self):
|
||||
self.right_mac_test()
|
||||
self.wrong_mac_test()
|
||||
self.zero_data()
|
||||
self.multiple_updates()
|
||||
self.no_mix_encrypt_decrypt()
|
||||
self.no_late_update()
|
||||
self.loopback()
|
||||
|
||||
def shortDescription(self):
|
||||
return self.description
|
||||
|
||||
class RoundtripTest(unittest.TestCase):
|
||||
def __init__(self, module, params):
|
||||
from Crypto import Random
|
||||
unittest.TestCase.__init__(self)
|
||||
self.module = module
|
||||
self.iv = Random.get_random_bytes(module.block_size)
|
||||
self.key = b(params['key'])
|
||||
self.plaintext = 100 * b(params['plaintext'])
|
||||
self.module_name = params.get('module_name', None)
|
||||
|
||||
def shortDescription(self):
|
||||
return """%s .decrypt() output of .encrypt() should not be garbled""" % (self.module_name,)
|
||||
|
||||
def runTest(self):
|
||||
for mode in (self.module.MODE_ECB, self.module.MODE_CBC, self.module.MODE_CFB, self.module.MODE_OFB, self.module.MODE_OPENPGP):
|
||||
encryption_cipher = self.module.new(a2b_hex(self.key), mode, self.iv)
|
||||
ciphertext = encryption_cipher.encrypt(self.plaintext)
|
||||
|
||||
if mode != self.module.MODE_OPENPGP:
|
||||
decryption_cipher = self.module.new(a2b_hex(self.key), mode, self.iv)
|
||||
else:
|
||||
eiv = ciphertext[:self.module.block_size+2]
|
||||
ciphertext = ciphertext[self.module.block_size+2:]
|
||||
decryption_cipher = self.module.new(a2b_hex(self.key), mode, eiv)
|
||||
decrypted_plaintext = decryption_cipher.decrypt(ciphertext)
|
||||
self.assertEqual(self.plaintext, decrypted_plaintext)
|
||||
|
||||
class PGPTest(unittest.TestCase):
|
||||
def __init__(self, module, params):
|
||||
unittest.TestCase.__init__(self)
|
||||
self.module = module
|
||||
self.key = b(params['key'])
|
||||
|
||||
def shortDescription(self):
|
||||
return "MODE_PGP was implemented incorrectly and insecurely. It's completely banished now."
|
||||
|
||||
def runTest(self):
|
||||
self.assertRaises(ValueError, self.module.new, a2b_hex(self.key),
|
||||
self.module.MODE_PGP)
|
||||
|
||||
class IVLengthTest(unittest.TestCase):
|
||||
def __init__(self, module, params):
|
||||
unittest.TestCase.__init__(self)
|
||||
self.module = module
|
||||
self.key = b(params['key'])
|
||||
|
||||
def shortDescription(self):
|
||||
return "Check that all modes except MODE_ECB and MODE_CTR require an IV of the proper length"
|
||||
|
||||
def runTest(self):
|
||||
self.assertRaises(ValueError, self.module.new, a2b_hex(self.key),
|
||||
self.module.MODE_CBC, "")
|
||||
self.assertRaises(ValueError, self.module.new, a2b_hex(self.key),
|
||||
self.module.MODE_CFB, "")
|
||||
self.assertRaises(ValueError, self.module.new, a2b_hex(self.key),
|
||||
self.module.MODE_OFB, "")
|
||||
self.assertRaises(ValueError, self.module.new, a2b_hex(self.key),
|
||||
self.module.MODE_OPENPGP, "")
|
||||
if hasattr(self.module, "MODE_CCM"):
|
||||
for ivlen in (0,6,14):
|
||||
self.assertRaises(ValueError, self.module.new, a2b_hex(self.key),
|
||||
self.module.MODE_CCM, bchr(0)*ivlen, msg_len=10)
|
||||
self.module.new(a2b_hex(self.key), self.module.MODE_ECB, "")
|
||||
self.module.new(a2b_hex(self.key), self.module.MODE_CTR, "", counter=self._dummy_counter)
|
||||
|
||||
def _dummy_counter(self):
|
||||
return "\0" * self.module.block_size
|
||||
|
||||
def make_block_tests(module, module_name, test_data, additional_params=dict()):
|
||||
tests = []
|
||||
extra_tests_added = 0
|
||||
for i in range(len(test_data)):
|
||||
row = test_data[i]
|
||||
|
||||
# Build the "params" dictionary
|
||||
params = {'mode': 'ECB'}
|
||||
if len(row) == 3:
|
||||
(params['plaintext'], params['ciphertext'], params['key']) = row
|
||||
elif len(row) == 4:
|
||||
(params['plaintext'], params['ciphertext'], params['key'], params['description']) = row
|
||||
elif len(row) == 5:
|
||||
(params['plaintext'], params['ciphertext'], params['key'], params['description'], extra_params) = row
|
||||
params.update(extra_params)
|
||||
else:
|
||||
raise AssertionError("Unsupported tuple size %d" % (len(row),))
|
||||
|
||||
# Build the display-name for the test
|
||||
p2 = params.copy()
|
||||
p_key = _extract(p2, 'key')
|
||||
p_plaintext = _extract(p2, 'plaintext')
|
||||
p_ciphertext = _extract(p2, 'ciphertext')
|
||||
p_description = _extract(p2, 'description', None)
|
||||
p_mode = p2.get('mode', 'ECB')
|
||||
if p_mode == 'ECB':
|
||||
_extract(p2, 'mode', 'ECB')
|
||||
|
||||
if p_description is not None:
|
||||
description = p_description
|
||||
elif p_mode == 'ECB' and not p2:
|
||||
description = "p=%s, k=%s" % (p_plaintext, p_key)
|
||||
else:
|
||||
description = "p=%s, k=%s, %r" % (p_plaintext, p_key, p2)
|
||||
name = "%s #%d: %s" % (module_name, i+1, description)
|
||||
params['description'] = name
|
||||
params['module_name'] = module_name
|
||||
params.update(additional_params)
|
||||
|
||||
# Add extra test(s) to the test suite before the current test
|
||||
if not extra_tests_added:
|
||||
tests += [
|
||||
CTRSegfaultTest(module, params),
|
||||
CTRWraparoundTest(module, params),
|
||||
CFBSegmentSizeTest(module, params),
|
||||
RoundtripTest(module, params),
|
||||
PGPTest(module, params),
|
||||
IVLengthTest(module, params),
|
||||
]
|
||||
extra_tests_added = 1
|
||||
|
||||
# Extract associated data and MAC for AEAD modes
|
||||
if p_mode in ('CCM', 'EAX', 'SIV', 'GCM'):
|
||||
assoc_data, params['plaintext'] = params['plaintext'].split('|')
|
||||
assoc_data2, params['ciphertext'], params['mac'] = params['ciphertext'].split('|')
|
||||
params['assoc_data'] = assoc_data.split("-")
|
||||
params['mac_len'] = len(params['mac'])>>1
|
||||
|
||||
# Add the current test to the test suite
|
||||
tests.append(CipherSelfTest(module, params))
|
||||
|
||||
# When using CTR mode, test that the interface behaves like a stream cipher
|
||||
if p_mode in ('OFB', 'CTR'):
|
||||
tests.append(CipherStreamingSelfTest(module, params))
|
||||
|
||||
# When using CTR mode, test the non-shortcut code path.
|
||||
if p_mode == 'CTR' and not params.has_key('ctr_class'):
|
||||
params2 = params.copy()
|
||||
params2['description'] += " (shortcut disabled)"
|
||||
ctr_params2 = params.get('ctr_params', {}).copy()
|
||||
params2['ctr_params'] = ctr_params2
|
||||
if not params2['ctr_params'].has_key('disable_shortcut'):
|
||||
params2['ctr_params']['disable_shortcut'] = 1
|
||||
tests.append(CipherSelfTest(module, params2))
|
||||
|
||||
# Add tests that don't use test vectors
|
||||
if hasattr(module, "MODE_CCM"):
|
||||
tests += [
|
||||
CCMMACLengthTest(module),
|
||||
CCMSplitEncryptionTest(module),
|
||||
]
|
||||
for aead_mode in ("MODE_CCM","MODE_EAX", "MODE_SIV", "MODE_GCM"):
|
||||
if hasattr(module, aead_mode):
|
||||
key_sizes = []
|
||||
try:
|
||||
key_sizes += module.key_size
|
||||
except TypeError:
|
||||
key_sizes = [ module.key_size ]
|
||||
for ks in key_sizes:
|
||||
tests += [
|
||||
AEADTests(module, aead_mode, ks),
|
||||
]
|
||||
|
||||
return tests
|
||||
|
||||
def make_stream_tests(module, module_name, test_data):
|
||||
tests = []
|
||||
for i in range(len(test_data)):
|
||||
row = test_data[i]
|
||||
|
||||
# Build the "params" dictionary
|
||||
params = {}
|
||||
if len(row) == 3:
|
||||
(params['plaintext'], params['ciphertext'], params['key']) = row
|
||||
elif len(row) == 4:
|
||||
(params['plaintext'], params['ciphertext'], params['key'], params['description']) = row
|
||||
elif len(row) == 5:
|
||||
(params['plaintext'], params['ciphertext'], params['key'], params['description'], extra_params) = row
|
||||
params.update(extra_params)
|
||||
else:
|
||||
raise AssertionError("Unsupported tuple size %d" % (len(row),))
|
||||
|
||||
# Build the display-name for the test
|
||||
p2 = params.copy()
|
||||
p_key = _extract(p2, 'key')
|
||||
p_plaintext = _extract(p2, 'plaintext')
|
||||
p_ciphertext = _extract(p2, 'ciphertext')
|
||||
p_description = _extract(p2, 'description', None)
|
||||
|
||||
if p_description is not None:
|
||||
description = p_description
|
||||
elif not p2:
|
||||
description = "p=%s, k=%s" % (p_plaintext, p_key)
|
||||
else:
|
||||
description = "p=%s, k=%s, %r" % (p_plaintext, p_key, p2)
|
||||
name = "%s #%d: %s" % (module_name, i+1, description)
|
||||
params['description'] = name
|
||||
params['module_name'] = module_name
|
||||
|
||||
# Add the test to the test suite
|
||||
tests.append(CipherSelfTest(module, params))
|
||||
tests.append(CipherStreamingSelfTest(module, params))
|
||||
return tests
|
||||
|
||||
# vim:set ts=4 sw=4 sts=4 expandtab:
|
2013
modules/Crypto/SelfTest/Cipher/test_AES.py
Normal file
2013
modules/Crypto/SelfTest/Cipher/test_AES.py
Normal file
File diff suppressed because it is too large
Load Diff
124
modules/Crypto/SelfTest/Cipher/test_ARC2.py
Normal file
124
modules/Crypto/SelfTest/Cipher/test_ARC2.py
Normal file
@ -0,0 +1,124 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# SelfTest/Cipher/ARC2.py: Self-test for the Alleged-RC2 cipher
|
||||
#
|
||||
# Written in 2008 by Dwayne C. Litzenberger <dlitz@dlitz.net>
|
||||
#
|
||||
# ===================================================================
|
||||
# The contents of this file are dedicated to the public domain. To
|
||||
# the extent that dedication to the public domain is not available,
|
||||
# everyone is granted a worldwide, perpetual, royalty-free,
|
||||
# non-exclusive license to exercise all rights associated with the
|
||||
# contents of this file for any purpose whatsoever.
|
||||
# No rights are reserved.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
# SOFTWARE.
|
||||
# ===================================================================
|
||||
|
||||
"""Self-test suite for Crypto.Cipher.ARC2"""
|
||||
|
||||
__revision__ = "$Id$"
|
||||
|
||||
from common import dict # For compatibility with Python 2.1 and 2.2
|
||||
|
||||
import unittest
|
||||
from Crypto.Util.py3compat import *
|
||||
|
||||
# This is a list of (plaintext, ciphertext, key[, description[, extra_params]]) tuples.
|
||||
test_data = [
|
||||
# Test vectors from RFC 2268
|
||||
|
||||
# 63-bit effective key length
|
||||
('0000000000000000', 'ebb773f993278eff', '0000000000000000',
|
||||
'RFC2268-1', dict(effective_keylen=63)),
|
||||
|
||||
# 64-bit effective key length
|
||||
('ffffffffffffffff', '278b27e42e2f0d49', 'ffffffffffffffff',
|
||||
'RFC2268-2', dict(effective_keylen=64)),
|
||||
('1000000000000001', '30649edf9be7d2c2', '3000000000000000',
|
||||
'RFC2268-3', dict(effective_keylen=64)),
|
||||
('0000000000000000', '61a8a244adacccf0', '88',
|
||||
'RFC2268-4', dict(effective_keylen=64)),
|
||||
('0000000000000000', '6ccf4308974c267f', '88bca90e90875a',
|
||||
'RFC2268-5', dict(effective_keylen=64)),
|
||||
('0000000000000000', '1a807d272bbe5db1', '88bca90e90875a7f0f79c384627bafb2',
|
||||
'RFC2268-6', dict(effective_keylen=64)),
|
||||
|
||||
# 128-bit effective key length
|
||||
('0000000000000000', '2269552ab0f85ca6', '88bca90e90875a7f0f79c384627bafb2',
|
||||
"RFC2268-7", dict(effective_keylen=128)),
|
||||
('0000000000000000', '5b78d3a43dfff1f1',
|
||||
'88bca90e90875a7f0f79c384627bafb216f80a6f85920584c42fceb0be255daf1e',
|
||||
"RFC2268-8", dict(effective_keylen=129)),
|
||||
|
||||
# Test vectors from PyCrypto 2.0.1's testdata.py
|
||||
# 1024-bit effective key length
|
||||
('0000000000000000', '624fb3e887419e48', '5068696c6970476c617373',
|
||||
'PCTv201-0'),
|
||||
('ffffffffffffffff', '79cadef44c4a5a85', '5068696c6970476c617373',
|
||||
'PCTv201-1'),
|
||||
('0001020304050607', '90411525b34e4c2c', '5068696c6970476c617373',
|
||||
'PCTv201-2'),
|
||||
('0011223344556677', '078656aaba61cbfb', '5068696c6970476c617373',
|
||||
'PCTv201-3'),
|
||||
('0000000000000000', 'd7bcc5dbb4d6e56a', 'ffffffffffffffff',
|
||||
'PCTv201-4'),
|
||||
('ffffffffffffffff', '7259018ec557b357', 'ffffffffffffffff',
|
||||
'PCTv201-5'),
|
||||
('0001020304050607', '93d20a497f2ccb62', 'ffffffffffffffff',
|
||||
'PCTv201-6'),
|
||||
('0011223344556677', 'cb15a7f819c0014d', 'ffffffffffffffff',
|
||||
'PCTv201-7'),
|
||||
('0000000000000000', '63ac98cdf3843a7a', 'ffffffffffffffff5065746572477265656e6177617953e5ffe553',
|
||||
'PCTv201-8'),
|
||||
('ffffffffffffffff', '3fb49e2fa12371dd', 'ffffffffffffffff5065746572477265656e6177617953e5ffe553',
|
||||
'PCTv201-9'),
|
||||
('0001020304050607', '46414781ab387d5f', 'ffffffffffffffff5065746572477265656e6177617953e5ffe553',
|
||||
'PCTv201-10'),
|
||||
('0011223344556677', 'be09dc81feaca271', 'ffffffffffffffff5065746572477265656e6177617953e5ffe553',
|
||||
'PCTv201-11'),
|
||||
('0000000000000000', 'e64221e608be30ab', '53e5ffe553',
|
||||
'PCTv201-12'),
|
||||
('ffffffffffffffff', '862bc60fdcd4d9a9', '53e5ffe553',
|
||||
'PCTv201-13'),
|
||||
('0001020304050607', '6a34da50fa5e47de', '53e5ffe553',
|
||||
'PCTv201-14'),
|
||||
('0011223344556677', '584644c34503122c', '53e5ffe553',
|
||||
'PCTv201-15'),
|
||||
]
|
||||
|
||||
class BufferOverflowTest(unittest.TestCase):
|
||||
# Test a buffer overflow found in older versions of PyCrypto
|
||||
|
||||
def setUp(self):
|
||||
global ARC2
|
||||
from Crypto.Cipher import ARC2
|
||||
|
||||
def runTest(self):
|
||||
"""ARC2 with keylength > 128"""
|
||||
key = "x" * 16384
|
||||
mode = ARC2.MODE_ECB
|
||||
self.assertRaises(ValueError, ARC2.new, key, mode)
|
||||
|
||||
def get_tests(config={}):
|
||||
from Crypto.Cipher import ARC2
|
||||
from common import make_block_tests
|
||||
|
||||
tests = make_block_tests(ARC2, "ARC2", test_data)
|
||||
tests.append(BufferOverflowTest())
|
||||
|
||||
return tests
|
||||
|
||||
if __name__ == '__main__':
|
||||
import unittest
|
||||
suite = lambda: unittest.TestSuite(get_tests())
|
||||
unittest.main(defaultTest='suite')
|
||||
|
||||
# vim:set ts=4 sw=4 sts=4 expandtab:
|
457
modules/Crypto/SelfTest/Cipher/test_ARC4.py
Normal file
457
modules/Crypto/SelfTest/Cipher/test_ARC4.py
Normal file
@ -0,0 +1,457 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# SelfTest/Cipher/ARC4.py: Self-test for the Alleged-RC4 cipher
|
||||
#
|
||||
# Written in 2008 by Dwayne C. Litzenberger <dlitz@dlitz.net>
|
||||
#
|
||||
# ===================================================================
|
||||
# The contents of this file are dedicated to the public domain. To
|
||||
# the extent that dedication to the public domain is not available,
|
||||
# everyone is granted a worldwide, perpetual, royalty-free,
|
||||
# non-exclusive license to exercise all rights associated with the
|
||||
# contents of this file for any purpose whatsoever.
|
||||
# No rights are reserved.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
# SOFTWARE.
|
||||
# ===================================================================
|
||||
|
||||
"""Self-test suite for Crypto.Cipher.ARC4"""
|
||||
|
||||
__revision__ = "$Id$"
|
||||
|
||||
from Crypto.Util.py3compat import *
|
||||
from Crypto.SelfTest.st_common import *
|
||||
from binascii import unhexlify
|
||||
|
||||
from Crypto.Cipher import ARC4
|
||||
|
||||
# This is a list of (plaintext, ciphertext, key[, description]) tuples.
|
||||
test_data = [
|
||||
# Test vectors from Eric Rescorla's message with the subject
|
||||
# "RC4 compatibility testing", sent to the cipherpunks mailing list on
|
||||
# September 13, 1994.
|
||||
# http://cypherpunks.venona.com/date/1994/09/msg00420.html
|
||||
|
||||
('0123456789abcdef', '75b7878099e0c596', '0123456789abcdef',
|
||||
'Test vector 0'),
|
||||
|
||||
('0000000000000000', '7494c2e7104b0879', '0123456789abcdef',
|
||||
'Test vector 1'),
|
||||
|
||||
('0000000000000000', 'de188941a3375d3a', '0000000000000000',
|
||||
'Test vector 2'),
|
||||
|
||||
('00000000000000000000', 'd6a141a7ec3c38dfbd61', 'ef012345',
|
||||
'Test vector 3'),
|
||||
|
||||
('01' * 512,
|
||||
'7595c3e6114a09780c4ad452338e1ffd9a1be9498f813d76533449b6778dcad8'
|
||||
+ 'c78a8d2ba9ac66085d0e53d59c26c2d1c490c1ebbe0ce66d1b6b1b13b6b919b8'
|
||||
+ '47c25a91447a95e75e4ef16779cde8bf0a95850e32af9689444fd377108f98fd'
|
||||
+ 'cbd4e726567500990bcc7e0ca3c4aaa304a387d20f3b8fbbcd42a1bd311d7a43'
|
||||
+ '03dda5ab078896ae80c18b0af66dff319616eb784e495ad2ce90d7f772a81747'
|
||||
+ 'b65f62093b1e0db9e5ba532fafec47508323e671327df9444432cb7367cec82f'
|
||||
+ '5d44c0d00b67d650a075cd4b70dedd77eb9b10231b6b5b741347396d62897421'
|
||||
+ 'd43df9b42e446e358e9c11a9b2184ecbef0cd8e7a877ef968f1390ec9b3d35a5'
|
||||
+ '585cb009290e2fcde7b5ec66d9084be44055a619d9dd7fc3166f9487f7cb2729'
|
||||
+ '12426445998514c15d53a18c864ce3a2b7555793988126520eacf2e3066e230c'
|
||||
+ '91bee4dd5304f5fd0405b35bd99c73135d3d9bc335ee049ef69b3867bf2d7bd1'
|
||||
+ 'eaa595d8bfc0066ff8d31509eb0c6caa006c807a623ef84c3d33c195d23ee320'
|
||||
+ 'c40de0558157c822d4b8c569d849aed59d4e0fd7f379586b4b7ff684ed6a189f'
|
||||
+ '7486d49b9c4bad9ba24b96abf924372c8a8fffb10d55354900a77a3db5f205e1'
|
||||
+ 'b99fcd8660863a159ad4abe40fa48934163ddde542a6585540fd683cbfd8c00f'
|
||||
+ '12129a284deacc4cdefe58be7137541c047126c8d49e2755ab181ab7e940b0c0',
|
||||
'0123456789abcdef',
|
||||
"Test vector 4"),
|
||||
]
|
||||
|
||||
class RFC6229_Tests(unittest.TestCase):
|
||||
# Test vectors from RFC 6229. Each test vector is a tuple with two items:
|
||||
# the ARC4 key and a dictionary. The dictionary has keystream offsets as keys
|
||||
# and the 16-byte keystream starting at the relevant offset as value.
|
||||
rfc6229_data = [
|
||||
# Page 3
|
||||
(
|
||||
'0102030405',
|
||||
{
|
||||
0: 'b2 39 63 05 f0 3d c0 27 cc c3 52 4a 0a 11 18 a8',
|
||||
16: '69 82 94 4f 18 fc 82 d5 89 c4 03 a4 7a 0d 09 19',
|
||||
240: '28 cb 11 32 c9 6c e2 86 42 1d ca ad b8 b6 9e ae',
|
||||
256: '1c fc f6 2b 03 ed db 64 1d 77 df cf 7f 8d 8c 93',
|
||||
496: '42 b7 d0 cd d9 18 a8 a3 3d d5 17 81 c8 1f 40 41',
|
||||
512: '64 59 84 44 32 a7 da 92 3c fb 3e b4 98 06 61 f6',
|
||||
752: 'ec 10 32 7b de 2b ee fd 18 f9 27 76 80 45 7e 22',
|
||||
768: 'eb 62 63 8d 4f 0b a1 fe 9f ca 20 e0 5b f8 ff 2b',
|
||||
1008:'45 12 90 48 e6 a0 ed 0b 56 b4 90 33 8f 07 8d a5',
|
||||
1024:'30 ab bc c7 c2 0b 01 60 9f 23 ee 2d 5f 6b b7 df',
|
||||
1520:'32 94 f7 44 d8 f9 79 05 07 e7 0f 62 e5 bb ce ea',
|
||||
1536:'d8 72 9d b4 18 82 25 9b ee 4f 82 53 25 f5 a1 30',
|
||||
2032:'1e b1 4a 0c 13 b3 bf 47 fa 2a 0b a9 3a d4 5b 8b',
|
||||
2048:'cc 58 2f 8b a9 f2 65 e2 b1 be 91 12 e9 75 d2 d7',
|
||||
3056:'f2 e3 0f 9b d1 02 ec bf 75 aa ad e9 bc 35 c4 3c',
|
||||
3072:'ec 0e 11 c4 79 dc 32 9d c8 da 79 68 fe 96 56 81',
|
||||
4080:'06 83 26 a2 11 84 16 d2 1f 9d 04 b2 cd 1c a0 50',
|
||||
4096:'ff 25 b5 89 95 99 67 07 e5 1f bd f0 8b 34 d8 75'
|
||||
}
|
||||
),
|
||||
# Page 4
|
||||
(
|
||||
'01020304050607',
|
||||
{
|
||||
0: '29 3f 02 d4 7f 37 c9 b6 33 f2 af 52 85 fe b4 6b',
|
||||
16: 'e6 20 f1 39 0d 19 bd 84 e2 e0 fd 75 20 31 af c1',
|
||||
240: '91 4f 02 53 1c 92 18 81 0d f6 0f 67 e3 38 15 4c',
|
||||
256: 'd0 fd b5 83 07 3c e8 5a b8 39 17 74 0e c0 11 d5',
|
||||
496: '75 f8 14 11 e8 71 cf fa 70 b9 0c 74 c5 92 e4 54',
|
||||
512: '0b b8 72 02 93 8d ad 60 9e 87 a5 a1 b0 79 e5 e4',
|
||||
752: 'c2 91 12 46 b6 12 e7 e7 b9 03 df ed a1 da d8 66',
|
||||
768: '32 82 8f 91 50 2b 62 91 36 8d e8 08 1d e3 6f c2',
|
||||
1008:'f3 b9 a7 e3 b2 97 bf 9a d8 04 51 2f 90 63 ef f1',
|
||||
1024:'8e cb 67 a9 ba 1f 55 a5 a0 67 e2 b0 26 a3 67 6f',
|
||||
1520:'d2 aa 90 2b d4 2d 0d 7c fd 34 0c d4 58 10 52 9f',
|
||||
1536:'78 b2 72 c9 6e 42 ea b4 c6 0b d9 14 e3 9d 06 e3',
|
||||
2032:'f4 33 2f d3 1a 07 93 96 ee 3c ee 3f 2a 4f f0 49',
|
||||
2048:'05 45 97 81 d4 1f da 7f 30 c1 be 7e 12 46 c6 23',
|
||||
3056:'ad fd 38 68 b8 e5 14 85 d5 e6 10 01 7e 3d d6 09',
|
||||
3072:'ad 26 58 1c 0c 5b e4 5f 4c ea 01 db 2f 38 05 d5',
|
||||
4080:'f3 17 2c ef fc 3b 3d 99 7c 85 cc d5 af 1a 95 0c',
|
||||
4096:'e7 4b 0b 97 31 22 7f d3 7c 0e c0 8a 47 dd d8 b8'
|
||||
}
|
||||
),
|
||||
(
|
||||
'0102030405060708',
|
||||
{
|
||||
0: '97 ab 8a 1b f0 af b9 61 32 f2 f6 72 58 da 15 a8',
|
||||
16: '82 63 ef db 45 c4 a1 86 84 ef 87 e6 b1 9e 5b 09',
|
||||
240: '96 36 eb c9 84 19 26 f4 f7 d1 f3 62 bd df 6e 18',
|
||||
256: 'd0 a9 90 ff 2c 05 fe f5 b9 03 73 c9 ff 4b 87 0a',
|
||||
496: '73 23 9f 1d b7 f4 1d 80 b6 43 c0 c5 25 18 ec 63',
|
||||
512: '16 3b 31 99 23 a6 bd b4 52 7c 62 61 26 70 3c 0f',
|
||||
752: '49 d6 c8 af 0f 97 14 4a 87 df 21 d9 14 72 f9 66',
|
||||
768: '44 17 3a 10 3b 66 16 c5 d5 ad 1c ee 40 c8 63 d0',
|
||||
1008:'27 3c 9c 4b 27 f3 22 e4 e7 16 ef 53 a4 7d e7 a4',
|
||||
1024:'c6 d0 e7 b2 26 25 9f a9 02 34 90 b2 61 67 ad 1d',
|
||||
1520:'1f e8 98 67 13 f0 7c 3d 9a e1 c1 63 ff 8c f9 d3',
|
||||
1536:'83 69 e1 a9 65 61 0b e8 87 fb d0 c7 91 62 aa fb',
|
||||
2032:'0a 01 27 ab b4 44 84 b9 fb ef 5a bc ae 1b 57 9f',
|
||||
2048:'c2 cd ad c6 40 2e 8e e8 66 e1 f3 7b db 47 e4 2c',
|
||||
3056:'26 b5 1e a3 7d f8 e1 d6 f7 6f c3 b6 6a 74 29 b3',
|
||||
3072:'bc 76 83 20 5d 4f 44 3d c1 f2 9d da 33 15 c8 7b',
|
||||
4080:'d5 fa 5a 34 69 d2 9a aa f8 3d 23 58 9d b8 c8 5b',
|
||||
4096:'3f b4 6e 2c 8f 0f 06 8e dc e8 cd cd 7d fc 58 62'
|
||||
}
|
||||
),
|
||||
# Page 5
|
||||
(
|
||||
'0102030405060708090a',
|
||||
{
|
||||
0: 'ed e3 b0 46 43 e5 86 cc 90 7d c2 18 51 70 99 02',
|
||||
16: '03 51 6b a7 8f 41 3b eb 22 3a a5 d4 d2 df 67 11',
|
||||
240: '3c fd 6c b5 8e e0 fd de 64 01 76 ad 00 00 04 4d',
|
||||
256: '48 53 2b 21 fb 60 79 c9 11 4c 0f fd 9c 04 a1 ad',
|
||||
496: '3e 8c ea 98 01 71 09 97 90 84 b1 ef 92 f9 9d 86',
|
||||
512: 'e2 0f b4 9b db 33 7e e4 8b 8d 8d c0 f4 af ef fe',
|
||||
752: '5c 25 21 ea cd 79 66 f1 5e 05 65 44 be a0 d3 15',
|
||||
768: 'e0 67 a7 03 19 31 a2 46 a6 c3 87 5d 2f 67 8a cb',
|
||||
1008:'a6 4f 70 af 88 ae 56 b6 f8 75 81 c0 e2 3e 6b 08',
|
||||
1024:'f4 49 03 1d e3 12 81 4e c6 f3 19 29 1f 4a 05 16',
|
||||
1520:'bd ae 85 92 4b 3c b1 d0 a2 e3 3a 30 c6 d7 95 99',
|
||||
1536:'8a 0f ed db ac 86 5a 09 bc d1 27 fb 56 2e d6 0a',
|
||||
2032:'b5 5a 0a 5b 51 a1 2a 8b e3 48 99 c3 e0 47 51 1a',
|
||||
2048:'d9 a0 9c ea 3c e7 5f e3 96 98 07 03 17 a7 13 39',
|
||||
3056:'55 22 25 ed 11 77 f4 45 84 ac 8c fa 6c 4e b5 fc',
|
||||
3072:'7e 82 cb ab fc 95 38 1b 08 09 98 44 21 29 c2 f8',
|
||||
4080:'1f 13 5e d1 4c e6 0a 91 36 9d 23 22 be f2 5e 3c',
|
||||
4096:'08 b6 be 45 12 4a 43 e2 eb 77 95 3f 84 dc 85 53'
|
||||
}
|
||||
),
|
||||
(
|
||||
'0102030405060708090a0b0c0d0e0f10',
|
||||
{
|
||||
0: '9a c7 cc 9a 60 9d 1e f7 b2 93 28 99 cd e4 1b 97',
|
||||
16: '52 48 c4 95 90 14 12 6a 6e 8a 84 f1 1d 1a 9e 1c',
|
||||
240: '06 59 02 e4 b6 20 f6 cc 36 c8 58 9f 66 43 2f 2b',
|
||||
256: 'd3 9d 56 6b c6 bc e3 01 07 68 15 15 49 f3 87 3f',
|
||||
496: 'b6 d1 e6 c4 a5 e4 77 1c ad 79 53 8d f2 95 fb 11',
|
||||
512: 'c6 8c 1d 5c 55 9a 97 41 23 df 1d bc 52 a4 3b 89',
|
||||
752: 'c5 ec f8 8d e8 97 fd 57 fe d3 01 70 1b 82 a2 59',
|
||||
768: 'ec cb e1 3d e1 fc c9 1c 11 a0 b2 6c 0b c8 fa 4d',
|
||||
1008:'e7 a7 25 74 f8 78 2a e2 6a ab cf 9e bc d6 60 65',
|
||||
1024:'bd f0 32 4e 60 83 dc c6 d3 ce dd 3c a8 c5 3c 16',
|
||||
1520:'b4 01 10 c4 19 0b 56 22 a9 61 16 b0 01 7e d2 97',
|
||||
1536:'ff a0 b5 14 64 7e c0 4f 63 06 b8 92 ae 66 11 81',
|
||||
2032:'d0 3d 1b c0 3c d3 3d 70 df f9 fa 5d 71 96 3e bd',
|
||||
2048:'8a 44 12 64 11 ea a7 8b d5 1e 8d 87 a8 87 9b f5',
|
||||
3056:'fa be b7 60 28 ad e2 d0 e4 87 22 e4 6c 46 15 a3',
|
||||
3072:'c0 5d 88 ab d5 03 57 f9 35 a6 3c 59 ee 53 76 23',
|
||||
4080:'ff 38 26 5c 16 42 c1 ab e8 d3 c2 fe 5e 57 2b f8',
|
||||
4096:'a3 6a 4c 30 1a e8 ac 13 61 0c cb c1 22 56 ca cc'
|
||||
}
|
||||
),
|
||||
# Page 6
|
||||
(
|
||||
'0102030405060708090a0b0c0d0e0f101112131415161718',
|
||||
{
|
||||
0: '05 95 e5 7f e5 f0 bb 3c 70 6e da c8 a4 b2 db 11',
|
||||
16: 'df de 31 34 4a 1a f7 69 c7 4f 07 0a ee 9e 23 26',
|
||||
240: 'b0 6b 9b 1e 19 5d 13 d8 f4 a7 99 5c 45 53 ac 05',
|
||||
256: '6b d2 37 8e c3 41 c9 a4 2f 37 ba 79 f8 8a 32 ff',
|
||||
496: 'e7 0b ce 1d f7 64 5a db 5d 2c 41 30 21 5c 35 22',
|
||||
512: '9a 57 30 c7 fc b4 c9 af 51 ff da 89 c7 f1 ad 22',
|
||||
752: '04 85 05 5f d4 f6 f0 d9 63 ef 5a b9 a5 47 69 82',
|
||||
768: '59 1f c6 6b cd a1 0e 45 2b 03 d4 55 1f 6b 62 ac',
|
||||
1008:'27 53 cc 83 98 8a fa 3e 16 88 a1 d3 b4 2c 9a 02',
|
||||
1024:'93 61 0d 52 3d 1d 3f 00 62 b3 c2 a3 bb c7 c7 f0',
|
||||
1520:'96 c2 48 61 0a ad ed fe af 89 78 c0 3d e8 20 5a',
|
||||
1536:'0e 31 7b 3d 1c 73 b9 e9 a4 68 8f 29 6d 13 3a 19',
|
||||
2032:'bd f0 e6 c3 cc a5 b5 b9 d5 33 b6 9c 56 ad a1 20',
|
||||
2048:'88 a2 18 b6 e2 ec e1 e6 24 6d 44 c7 59 d1 9b 10',
|
||||
3056:'68 66 39 7e 95 c1 40 53 4f 94 26 34 21 00 6e 40',
|
||||
3072:'32 cb 0a 1e 95 42 c6 b3 b8 b3 98 ab c3 b0 f1 d5',
|
||||
4080:'29 a0 b8 ae d5 4a 13 23 24 c6 2e 42 3f 54 b4 c8',
|
||||
4096:'3c b0 f3 b5 02 0a 98 b8 2a f9 fe 15 44 84 a1 68'
|
||||
}
|
||||
),
|
||||
(
|
||||
'0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20',
|
||||
{
|
||||
0: 'ea a6 bd 25 88 0b f9 3d 3f 5d 1e 4c a2 61 1d 91',
|
||||
16: 'cf a4 5c 9f 7e 71 4b 54 bd fa 80 02 7c b1 43 80',
|
||||
240: '11 4a e3 44 de d7 1b 35 f2 e6 0f eb ad 72 7f d8',
|
||||
256: '02 e1 e7 05 6b 0f 62 39 00 49 64 22 94 3e 97 b6',
|
||||
496: '91 cb 93 c7 87 96 4e 10 d9 52 7d 99 9c 6f 93 6b',
|
||||
512: '49 b1 8b 42 f8 e8 36 7c be b5 ef 10 4b a1 c7 cd',
|
||||
752: '87 08 4b 3b a7 00 ba de 95 56 10 67 27 45 b3 74',
|
||||
768: 'e7 a7 b9 e9 ec 54 0d 5f f4 3b db 12 79 2d 1b 35',
|
||||
1008:'c7 99 b5 96 73 8f 6b 01 8c 76 c7 4b 17 59 bd 90',
|
||||
1024:'7f ec 5b fd 9f 9b 89 ce 65 48 30 90 92 d7 e9 58',
|
||||
1520:'40 f2 50 b2 6d 1f 09 6a 4a fd 4c 34 0a 58 88 15',
|
||||
1536:'3e 34 13 5c 79 db 01 02 00 76 76 51 cf 26 30 73',
|
||||
2032:'f6 56 ab cc f8 8d d8 27 02 7b 2c e9 17 d4 64 ec',
|
||||
2048:'18 b6 25 03 bf bc 07 7f ba bb 98 f2 0d 98 ab 34',
|
||||
3056:'8a ed 95 ee 5b 0d cb fb ef 4e b2 1d 3a 3f 52 f9',
|
||||
3072:'62 5a 1a b0 0e e3 9a 53 27 34 6b dd b0 1a 9c 18',
|
||||
4080:'a1 3a 7c 79 c7 e1 19 b5 ab 02 96 ab 28 c3 00 b9',
|
||||
4096:'f3 e4 c0 a2 e0 2d 1d 01 f7 f0 a7 46 18 af 2b 48'
|
||||
}
|
||||
),
|
||||
# Page 7
|
||||
(
|
||||
'833222772a',
|
||||
{
|
||||
0: '80 ad 97 bd c9 73 df 8a 2e 87 9e 92 a4 97 ef da',
|
||||
16: '20 f0 60 c2 f2 e5 12 65 01 d3 d4 fe a1 0d 5f c0',
|
||||
240: 'fa a1 48 e9 90 46 18 1f ec 6b 20 85 f3 b2 0e d9',
|
||||
256: 'f0 da f5 ba b3 d5 96 83 98 57 84 6f 73 fb fe 5a',
|
||||
496: '1c 7e 2f c4 63 92 32 fe 29 75 84 b2 96 99 6b c8',
|
||||
512: '3d b9 b2 49 40 6c c8 ed ff ac 55 cc d3 22 ba 12',
|
||||
752: 'e4 f9 f7 e0 06 61 54 bb d1 25 b7 45 56 9b c8 97',
|
||||
768: '75 d5 ef 26 2b 44 c4 1a 9c f6 3a e1 45 68 e1 b9',
|
||||
1008:'6d a4 53 db f8 1e 82 33 4a 3d 88 66 cb 50 a1 e3',
|
||||
1024:'78 28 d0 74 11 9c ab 5c 22 b2 94 d7 a9 bf a0 bb',
|
||||
1520:'ad b8 9c ea 9a 15 fb e6 17 29 5b d0 4b 8c a0 5c',
|
||||
1536:'62 51 d8 7f d4 aa ae 9a 7e 4a d5 c2 17 d3 f3 00',
|
||||
2032:'e7 11 9b d6 dd 9b 22 af e8 f8 95 85 43 28 81 e2',
|
||||
2048:'78 5b 60 fd 7e c4 e9 fc b6 54 5f 35 0d 66 0f ab',
|
||||
3056:'af ec c0 37 fd b7 b0 83 8e b3 d7 0b cd 26 83 82',
|
||||
3072:'db c1 a7 b4 9d 57 35 8c c9 fa 6d 61 d7 3b 7c f0',
|
||||
4080:'63 49 d1 26 a3 7a fc ba 89 79 4f 98 04 91 4f dc',
|
||||
4096:'bf 42 c3 01 8c 2f 7c 66 bf de 52 49 75 76 81 15'
|
||||
}
|
||||
),
|
||||
(
|
||||
'1910833222772a',
|
||||
{
|
||||
0: 'bc 92 22 db d3 27 4d 8f c6 6d 14 cc bd a6 69 0b',
|
||||
16: '7a e6 27 41 0c 9a 2b e6 93 df 5b b7 48 5a 63 e3',
|
||||
240: '3f 09 31 aa 03 de fb 30 0f 06 01 03 82 6f 2a 64',
|
||||
256: 'be aa 9e c8 d5 9b b6 81 29 f3 02 7c 96 36 11 81',
|
||||
496: '74 e0 4d b4 6d 28 64 8d 7d ee 8a 00 64 b0 6c fe',
|
||||
512: '9b 5e 81 c6 2f e0 23 c5 5b e4 2f 87 bb f9 32 b8',
|
||||
752: 'ce 17 8f c1 82 6e fe cb c1 82 f5 79 99 a4 61 40',
|
||||
768: '8b df 55 cd 55 06 1c 06 db a6 be 11 de 4a 57 8a',
|
||||
1008:'62 6f 5f 4d ce 65 25 01 f3 08 7d 39 c9 2c c3 49',
|
||||
1024:'42 da ac 6a 8f 9a b9 a7 fd 13 7c 60 37 82 56 82',
|
||||
1520:'cc 03 fd b7 91 92 a2 07 31 2f 53 f5 d4 dc 33 d9',
|
||||
1536:'f7 0f 14 12 2a 1c 98 a3 15 5d 28 b8 a0 a8 a4 1d',
|
||||
2032:'2a 3a 30 7a b2 70 8a 9c 00 fe 0b 42 f9 c2 d6 a1',
|
||||
2048:'86 26 17 62 7d 22 61 ea b0 b1 24 65 97 ca 0a e9',
|
||||
3056:'55 f8 77 ce 4f 2e 1d db bf 8e 13 e2 cd e0 fd c8',
|
||||
3072:'1b 15 56 cb 93 5f 17 33 37 70 5f bb 5d 50 1f c1',
|
||||
4080:'ec d0 e9 66 02 be 7f 8d 50 92 81 6c cc f2 c2 e9',
|
||||
4096:'02 78 81 fa b4 99 3a 1c 26 20 24 a9 4f ff 3f 61'
|
||||
}
|
||||
),
|
||||
# Page 8
|
||||
(
|
||||
'641910833222772a',
|
||||
{
|
||||
0: 'bb f6 09 de 94 13 17 2d 07 66 0c b6 80 71 69 26',
|
||||
16: '46 10 1a 6d ab 43 11 5d 6c 52 2b 4f e9 36 04 a9',
|
||||
240: 'cb e1 ff f2 1c 96 f3 ee f6 1e 8f e0 54 2c bd f0',
|
||||
256: '34 79 38 bf fa 40 09 c5 12 cf b4 03 4b 0d d1 a7',
|
||||
496: '78 67 a7 86 d0 0a 71 47 90 4d 76 dd f1 e5 20 e3',
|
||||
512: '8d 3e 9e 1c ae fc cc b3 fb f8 d1 8f 64 12 0b 32',
|
||||
752: '94 23 37 f8 fd 76 f0 fa e8 c5 2d 79 54 81 06 72',
|
||||
768: 'b8 54 8c 10 f5 16 67 f6 e6 0e 18 2f a1 9b 30 f7',
|
||||
1008:'02 11 c7 c6 19 0c 9e fd 12 37 c3 4c 8f 2e 06 c4',
|
||||
1024:'bd a6 4f 65 27 6d 2a ac b8 f9 02 12 20 3a 80 8e',
|
||||
1520:'bd 38 20 f7 32 ff b5 3e c1 93 e7 9d 33 e2 7c 73',
|
||||
1536:'d0 16 86 16 86 19 07 d4 82 e3 6c da c8 cf 57 49',
|
||||
2032:'97 b0 f0 f2 24 b2 d2 31 71 14 80 8f b0 3a f7 a0',
|
||||
2048:'e5 96 16 e4 69 78 79 39 a0 63 ce ea 9a f9 56 d1',
|
||||
3056:'c4 7e 0d c1 66 09 19 c1 11 01 20 8f 9e 69 aa 1f',
|
||||
3072:'5a e4 f1 28 96 b8 37 9a 2a ad 89 b5 b5 53 d6 b0',
|
||||
4080:'6b 6b 09 8d 0c 29 3b c2 99 3d 80 bf 05 18 b6 d9',
|
||||
4096:'81 70 cc 3c cd 92 a6 98 62 1b 93 9d d3 8f e7 b9'
|
||||
}
|
||||
),
|
||||
(
|
||||
'8b37641910833222772a',
|
||||
{
|
||||
0: 'ab 65 c2 6e dd b2 87 60 0d b2 fd a1 0d 1e 60 5c',
|
||||
16: 'bb 75 90 10 c2 96 58 f2 c7 2d 93 a2 d1 6d 29 30',
|
||||
240: 'b9 01 e8 03 6e d1 c3 83 cd 3c 4c 4d d0 a6 ab 05',
|
||||
256: '3d 25 ce 49 22 92 4c 55 f0 64 94 33 53 d7 8a 6c',
|
||||
496: '12 c1 aa 44 bb f8 7e 75 e6 11 f6 9b 2c 38 f4 9b',
|
||||
512: '28 f2 b3 43 4b 65 c0 98 77 47 00 44 c6 ea 17 0d',
|
||||
752: 'bd 9e f8 22 de 52 88 19 61 34 cf 8a f7 83 93 04',
|
||||
768: '67 55 9c 23 f0 52 15 84 70 a2 96 f7 25 73 5a 32',
|
||||
1008:'8b ab 26 fb c2 c1 2b 0f 13 e2 ab 18 5e ab f2 41',
|
||||
1024:'31 18 5a 6d 69 6f 0c fa 9b 42 80 8b 38 e1 32 a2',
|
||||
1520:'56 4d 3d ae 18 3c 52 34 c8 af 1e 51 06 1c 44 b5',
|
||||
1536:'3c 07 78 a7 b5 f7 2d 3c 23 a3 13 5c 7d 67 b9 f4',
|
||||
2032:'f3 43 69 89 0f cf 16 fb 51 7d ca ae 44 63 b2 dd',
|
||||
2048:'02 f3 1c 81 e8 20 07 31 b8 99 b0 28 e7 91 bf a7',
|
||||
3056:'72 da 64 62 83 22 8c 14 30 08 53 70 17 95 61 6f',
|
||||
3072:'4e 0a 8c 6f 79 34 a7 88 e2 26 5e 81 d6 d0 c8 f4',
|
||||
4080:'43 8d d5 ea fe a0 11 1b 6f 36 b4 b9 38 da 2a 68',
|
||||
4096:'5f 6b fc 73 81 58 74 d9 71 00 f0 86 97 93 57 d8'
|
||||
}
|
||||
),
|
||||
# Page 9
|
||||
(
|
||||
'ebb46227c6cc8b37641910833222772a',
|
||||
{
|
||||
0: '72 0c 94 b6 3e df 44 e1 31 d9 50 ca 21 1a 5a 30',
|
||||
16: 'c3 66 fd ea cf 9c a8 04 36 be 7c 35 84 24 d2 0b',
|
||||
240: 'b3 39 4a 40 aa bf 75 cb a4 22 82 ef 25 a0 05 9f',
|
||||
256: '48 47 d8 1d a4 94 2d bc 24 9d ef c4 8c 92 2b 9f',
|
||||
496: '08 12 8c 46 9f 27 53 42 ad da 20 2b 2b 58 da 95',
|
||||
512: '97 0d ac ef 40 ad 98 72 3b ac 5d 69 55 b8 17 61',
|
||||
752: '3c b8 99 93 b0 7b 0c ed 93 de 13 d2 a1 10 13 ac',
|
||||
768: 'ef 2d 67 6f 15 45 c2 c1 3d c6 80 a0 2f 4a db fe',
|
||||
1008:'b6 05 95 51 4f 24 bc 9f e5 22 a6 ca d7 39 36 44',
|
||||
1024:'b5 15 a8 c5 01 17 54 f5 90 03 05 8b db 81 51 4e',
|
||||
1520:'3c 70 04 7e 8c bc 03 8e 3b 98 20 db 60 1d a4 95',
|
||||
1536:'11 75 da 6e e7 56 de 46 a5 3e 2b 07 56 60 b7 70',
|
||||
2032:'00 a5 42 bb a0 21 11 cc 2c 65 b3 8e bd ba 58 7e',
|
||||
2048:'58 65 fd bb 5b 48 06 41 04 e8 30 b3 80 f2 ae de',
|
||||
3056:'34 b2 1a d2 ad 44 e9 99 db 2d 7f 08 63 f0 d9 b6',
|
||||
3072:'84 a9 21 8f c3 6e 8a 5f 2c cf be ae 53 a2 7d 25',
|
||||
4080:'a2 22 1a 11 b8 33 cc b4 98 a5 95 40 f0 54 5f 4a',
|
||||
4096:'5b be b4 78 7d 59 e5 37 3f db ea 6c 6f 75 c2 9b'
|
||||
}
|
||||
),
|
||||
(
|
||||
'c109163908ebe51debb46227c6cc8b37641910833222772a',
|
||||
{
|
||||
0: '54 b6 4e 6b 5a 20 b5 e2 ec 84 59 3d c7 98 9d a7',
|
||||
16: 'c1 35 ee e2 37 a8 54 65 ff 97 dc 03 92 4f 45 ce',
|
||||
240: 'cf cc 92 2f b4 a1 4a b4 5d 61 75 aa bb f2 d2 01',
|
||||
256: '83 7b 87 e2 a4 46 ad 0e f7 98 ac d0 2b 94 12 4f',
|
||||
496: '17 a6 db d6 64 92 6a 06 36 b3 f4 c3 7a 4f 46 94',
|
||||
512: '4a 5f 9f 26 ae ee d4 d4 a2 5f 63 2d 30 52 33 d9',
|
||||
752: '80 a3 d0 1e f0 0c 8e 9a 42 09 c1 7f 4e eb 35 8c',
|
||||
768: 'd1 5e 7d 5f fa aa bc 02 07 bf 20 0a 11 77 93 a2',
|
||||
1008:'34 96 82 bf 58 8e aa 52 d0 aa 15 60 34 6a ea fa',
|
||||
1024:'f5 85 4c db 76 c8 89 e3 ad 63 35 4e 5f 72 75 e3',
|
||||
1520:'53 2c 7c ec cb 39 df 32 36 31 84 05 a4 b1 27 9c',
|
||||
1536:'ba ef e6 d9 ce b6 51 84 22 60 e0 d1 e0 5e 3b 90',
|
||||
2032:'e8 2d 8c 6d b5 4e 3c 63 3f 58 1c 95 2b a0 42 07',
|
||||
2048:'4b 16 e5 0a bd 38 1b d7 09 00 a9 cd 9a 62 cb 23',
|
||||
3056:'36 82 ee 33 bd 14 8b d9 f5 86 56 cd 8f 30 d9 fb',
|
||||
3072:'1e 5a 0b 84 75 04 5d 9b 20 b2 62 86 24 ed fd 9e',
|
||||
4080:'63 ed d6 84 fb 82 62 82 fe 52 8f 9c 0e 92 37 bc',
|
||||
4096:'e4 dd 2e 98 d6 96 0f ae 0b 43 54 54 56 74 33 91'
|
||||
}
|
||||
),
|
||||
# Page 10
|
||||
(
|
||||
'1ada31d5cf688221c109163908ebe51debb46227c6cc8b37641910833222772a',
|
||||
{
|
||||
0: 'dd 5b cb 00 18 e9 22 d4 94 75 9d 7c 39 5d 02 d3',
|
||||
16: 'c8 44 6f 8f 77 ab f7 37 68 53 53 eb 89 a1 c9 eb',
|
||||
240: 'af 3e 30 f9 c0 95 04 59 38 15 15 75 c3 fb 90 98',
|
||||
256: 'f8 cb 62 74 db 99 b8 0b 1d 20 12 a9 8e d4 8f 0e',
|
||||
496: '25 c3 00 5a 1c b8 5d e0 76 25 98 39 ab 71 98 ab',
|
||||
512: '9d cb c1 83 e8 cb 99 4b 72 7b 75 be 31 80 76 9c',
|
||||
752: 'a1 d3 07 8d fa 91 69 50 3e d9 d4 49 1d ee 4e b2',
|
||||
768: '85 14 a5 49 58 58 09 6f 59 6e 4b cd 66 b1 06 65',
|
||||
1008:'5f 40 d5 9e c1 b0 3b 33 73 8e fa 60 b2 25 5d 31',
|
||||
1024:'34 77 c7 f7 64 a4 1b ac ef f9 0b f1 4f 92 b7 cc',
|
||||
1520:'ac 4e 95 36 8d 99 b9 eb 78 b8 da 8f 81 ff a7 95',
|
||||
1536:'8c 3c 13 f8 c2 38 8b b7 3f 38 57 6e 65 b7 c4 46',
|
||||
2032:'13 c4 b9 c1 df b6 65 79 ed dd 8a 28 0b 9f 73 16',
|
||||
2048:'dd d2 78 20 55 01 26 69 8e fa ad c6 4b 64 f6 6e',
|
||||
3056:'f0 8f 2e 66 d2 8e d1 43 f3 a2 37 cf 9d e7 35 59',
|
||||
3072:'9e a3 6c 52 55 31 b8 80 ba 12 43 34 f5 7b 0b 70',
|
||||
4080:'d5 a3 9e 3d fc c5 02 80 ba c4 a6 b5 aa 0d ca 7d',
|
||||
4096:'37 0b 1c 1f e6 55 91 6d 97 fd 0d 47 ca 1d 72 b8'
|
||||
}
|
||||
)
|
||||
]
|
||||
|
||||
def test_keystream(self):
|
||||
for tv in self.rfc6229_data:
|
||||
key = unhexlify(b((tv[0])))
|
||||
cipher = ARC4.new(key)
|
||||
count = 0
|
||||
for offset in range(0,4096+1,16):
|
||||
ct = cipher.encrypt(b('\x00')*16)
|
||||
expected = tv[1].get(offset)
|
||||
if expected:
|
||||
expected = unhexlify(b(expected.replace(" ",'')))
|
||||
self.assertEquals(ct, expected)
|
||||
count += 1
|
||||
self.assertEqual(count, len(tv[1]))
|
||||
|
||||
class Drop_Tests(unittest.TestCase):
|
||||
key = b('\xAA')*16
|
||||
data = b('\x00')*5000
|
||||
|
||||
def setUp(self):
|
||||
self.cipher = ARC4.new(self.key)
|
||||
|
||||
def test_drop256_encrypt(self):
|
||||
cipher_drop = ARC4.new(self.key, 256)
|
||||
ct_drop = cipher_drop.encrypt(self.data[:16])
|
||||
ct = self.cipher.encrypt(self.data)[256:256+16]
|
||||
self.assertEquals(ct_drop, ct)
|
||||
|
||||
def test_drop256_decrypt(self):
|
||||
cipher_drop = ARC4.new(self.key, 256)
|
||||
pt_drop = cipher_drop.decrypt(self.data[:16])
|
||||
pt = self.cipher.decrypt(self.data)[256:256+16]
|
||||
self.assertEquals(pt_drop, pt)
|
||||
|
||||
def get_tests(config={}):
|
||||
from common import make_stream_tests
|
||||
tests = make_stream_tests(ARC4, "ARC4", test_data)
|
||||
tests += list_test_cases(RFC6229_Tests)
|
||||
tests += list_test_cases(Drop_Tests)
|
||||
return tests
|
||||
|
||||
if __name__ == '__main__':
|
||||
import unittest
|
||||
suite = lambda: unittest.TestSuite(get_tests())
|
||||
unittest.main(defaultTest='suite')
|
||||
|
||||
# vim:set ts=4 sw=4 sts=4 expandtab:
|
113
modules/Crypto/SelfTest/Cipher/test_Blowfish.py
Normal file
113
modules/Crypto/SelfTest/Cipher/test_Blowfish.py
Normal file
@ -0,0 +1,113 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# SelfTest/Cipher/test_Blowfish.py: Self-test for the Blowfish cipher
|
||||
#
|
||||
# Written in 2008 by Dwayne C. Litzenberger <dlitz@dlitz.net>
|
||||
#
|
||||
# ===================================================================
|
||||
# The contents of this file are dedicated to the public domain. To
|
||||
# the extent that dedication to the public domain is not available,
|
||||
# everyone is granted a worldwide, perpetual, royalty-free,
|
||||
# non-exclusive license to exercise all rights associated with the
|
||||
# contents of this file for any purpose whatsoever.
|
||||
# No rights are reserved.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
# SOFTWARE.
|
||||
# ===================================================================
|
||||
|
||||
"""Self-test suite for Crypto.Cipher.Blowfish"""
|
||||
|
||||
__revision__ = "$Id$"
|
||||
|
||||
from Crypto.Util.py3compat import *
|
||||
|
||||
# This is a list of (plaintext, ciphertext, key) tuples.
|
||||
test_data = [
|
||||
# Test vectors from http://www.schneier.com/code/vectors.txt
|
||||
('0000000000000000', '4ef997456198dd78', '0000000000000000'),
|
||||
('ffffffffffffffff', '51866fd5b85ecb8a', 'ffffffffffffffff'),
|
||||
('1000000000000001', '7d856f9a613063f2', '3000000000000000'),
|
||||
('1111111111111111', '2466dd878b963c9d', '1111111111111111'),
|
||||
('1111111111111111', '61f9c3802281b096', '0123456789abcdef'),
|
||||
('0123456789abcdef', '7d0cc630afda1ec7', '1111111111111111'),
|
||||
('0000000000000000', '4ef997456198dd78', '0000000000000000'),
|
||||
('0123456789abcdef', '0aceab0fc6a0a28d', 'fedcba9876543210'),
|
||||
('01a1d6d039776742', '59c68245eb05282b', '7ca110454a1a6e57'),
|
||||
('5cd54ca83def57da', 'b1b8cc0b250f09a0', '0131d9619dc1376e'),
|
||||
('0248d43806f67172', '1730e5778bea1da4', '07a1133e4a0b2686'),
|
||||
('51454b582ddf440a', 'a25e7856cf2651eb', '3849674c2602319e'),
|
||||
('42fd443059577fa2', '353882b109ce8f1a', '04b915ba43feb5b6'),
|
||||
('059b5e0851cf143a', '48f4d0884c379918', '0113b970fd34f2ce'),
|
||||
('0756d8e0774761d2', '432193b78951fc98', '0170f175468fb5e6'),
|
||||
('762514b829bf486a', '13f04154d69d1ae5', '43297fad38e373fe'),
|
||||
('3bdd119049372802', '2eedda93ffd39c79', '07a7137045da2a16'),
|
||||
('26955f6835af609a', 'd887e0393c2da6e3', '04689104c2fd3b2f'),
|
||||
('164d5e404f275232', '5f99d04f5b163969', '37d06bb516cb7546'),
|
||||
('6b056e18759f5cca', '4a057a3b24d3977b', '1f08260d1ac2465e'),
|
||||
('004bd6ef09176062', '452031c1e4fada8e', '584023641aba6176'),
|
||||
('480d39006ee762f2', '7555ae39f59b87bd', '025816164629b007'),
|
||||
('437540c8698f3cfa', '53c55f9cb49fc019', '49793ebc79b3258f'),
|
||||
('072d43a077075292', '7a8e7bfa937e89a3', '4fb05e1515ab73a7'),
|
||||
('02fe55778117f12a', 'cf9c5d7a4986adb5', '49e95d6d4ca229bf'),
|
||||
('1d9d5c5018f728c2', 'd1abb290658bc778', '018310dc409b26d6'),
|
||||
('305532286d6f295a', '55cb3774d13ef201', '1c587f1c13924fef'),
|
||||
('0123456789abcdef', 'fa34ec4847b268b2', '0101010101010101'),
|
||||
('0123456789abcdef', 'a790795108ea3cae', '1f1f1f1f0e0e0e0e'),
|
||||
('0123456789abcdef', 'c39e072d9fac631d', 'e0fee0fef1fef1fe'),
|
||||
('ffffffffffffffff', '014933e0cdaff6e4', '0000000000000000'),
|
||||
('0000000000000000', 'f21e9a77b71c49bc', 'ffffffffffffffff'),
|
||||
('0000000000000000', '245946885754369a', '0123456789abcdef'),
|
||||
('ffffffffffffffff', '6b5c5a9c5d9e0a5a', 'fedcba9876543210'),
|
||||
('fedcba9876543210', 'f9ad597c49db005e', 'f0'),
|
||||
('fedcba9876543210', 'e91d21c1d961a6d6', 'f0e1'),
|
||||
('fedcba9876543210', 'e9c2b70a1bc65cf3', 'f0e1d2'),
|
||||
('fedcba9876543210', 'be1e639408640f05', 'f0e1d2c3'),
|
||||
('fedcba9876543210', 'b39e44481bdb1e6e', 'f0e1d2c3b4'),
|
||||
('fedcba9876543210', '9457aa83b1928c0d', 'f0e1d2c3b4a5'),
|
||||
('fedcba9876543210', '8bb77032f960629d', 'f0e1d2c3b4a596'),
|
||||
('fedcba9876543210', 'e87a244e2cc85e82', 'f0e1d2c3b4a59687'),
|
||||
('fedcba9876543210', '15750e7a4f4ec577', 'f0e1d2c3b4a5968778'),
|
||||
('fedcba9876543210', '122ba70b3ab64ae0', 'f0e1d2c3b4a596877869'),
|
||||
('fedcba9876543210', '3a833c9affc537f6', 'f0e1d2c3b4a5968778695a'),
|
||||
('fedcba9876543210', '9409da87a90f6bf2', 'f0e1d2c3b4a5968778695a4b'),
|
||||
('fedcba9876543210', '884f80625060b8b4', 'f0e1d2c3b4a5968778695a4b3c'),
|
||||
('fedcba9876543210', '1f85031c19e11968', 'f0e1d2c3b4a5968778695a4b3c2d'),
|
||||
('fedcba9876543210', '79d9373a714ca34f', 'f0e1d2c3b4a5968778695a4b3c2d1e'),
|
||||
('fedcba9876543210', '93142887ee3be15c',
|
||||
'f0e1d2c3b4a5968778695a4b3c2d1e0f'),
|
||||
('fedcba9876543210', '03429e838ce2d14b',
|
||||
'f0e1d2c3b4a5968778695a4b3c2d1e0f00'),
|
||||
('fedcba9876543210', 'a4299e27469ff67b',
|
||||
'f0e1d2c3b4a5968778695a4b3c2d1e0f0011'),
|
||||
('fedcba9876543210', 'afd5aed1c1bc96a8',
|
||||
'f0e1d2c3b4a5968778695a4b3c2d1e0f001122'),
|
||||
('fedcba9876543210', '10851c0e3858da9f',
|
||||
'f0e1d2c3b4a5968778695a4b3c2d1e0f00112233'),
|
||||
('fedcba9876543210', 'e6f51ed79b9db21f',
|
||||
'f0e1d2c3b4a5968778695a4b3c2d1e0f0011223344'),
|
||||
('fedcba9876543210', '64a6e14afd36b46f',
|
||||
'f0e1d2c3b4a5968778695a4b3c2d1e0f001122334455'),
|
||||
('fedcba9876543210', '80c7d7d45a5479ad',
|
||||
'f0e1d2c3b4a5968778695a4b3c2d1e0f00112233445566'),
|
||||
('fedcba9876543210', '05044b62fa52d080',
|
||||
'f0e1d2c3b4a5968778695a4b3c2d1e0f0011223344556677'),
|
||||
]
|
||||
|
||||
def get_tests(config={}):
|
||||
from Crypto.Cipher import Blowfish
|
||||
from common import make_block_tests
|
||||
return make_block_tests(Blowfish, "Blowfish", test_data)
|
||||
|
||||
if __name__ == '__main__':
|
||||
import unittest
|
||||
suite = lambda: unittest.TestSuite(get_tests())
|
||||
unittest.main(defaultTest='suite')
|
||||
|
||||
# vim:set ts=4 sw=4 sts=4 expandtab:
|
57
modules/Crypto/SelfTest/Cipher/test_CAST.py
Normal file
57
modules/Crypto/SelfTest/Cipher/test_CAST.py
Normal file
@ -0,0 +1,57 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# SelfTest/Cipher/CAST.py: Self-test for the CAST-128 (CAST5) cipher
|
||||
#
|
||||
# Written in 2008 by Dwayne C. Litzenberger <dlitz@dlitz.net>
|
||||
#
|
||||
# ===================================================================
|
||||
# The contents of this file are dedicated to the public domain. To
|
||||
# the extent that dedication to the public domain is not available,
|
||||
# everyone is granted a worldwide, perpetual, royalty-free,
|
||||
# non-exclusive license to exercise all rights associated with the
|
||||
# contents of this file for any purpose whatsoever.
|
||||
# No rights are reserved.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
# SOFTWARE.
|
||||
# ===================================================================
|
||||
|
||||
"""Self-test suite for Crypto.Cipher.CAST"""
|
||||
|
||||
__revision__ = "$Id$"
|
||||
|
||||
from Crypto.Util.py3compat import *
|
||||
|
||||
# This is a list of (plaintext, ciphertext, key) tuples.
|
||||
test_data = [
|
||||
# Test vectors from RFC 2144, B.1
|
||||
('0123456789abcdef', '238b4fe5847e44b2',
|
||||
'0123456712345678234567893456789a',
|
||||
'128-bit key'),
|
||||
|
||||
('0123456789abcdef', 'eb6a711a2c02271b',
|
||||
'01234567123456782345',
|
||||
'80-bit key'),
|
||||
|
||||
('0123456789abcdef', '7ac816d16e9b302e',
|
||||
'0123456712',
|
||||
'40-bit key'),
|
||||
]
|
||||
|
||||
def get_tests(config={}):
|
||||
from Crypto.Cipher import CAST
|
||||
from common import make_block_tests
|
||||
return make_block_tests(CAST, "CAST", test_data)
|
||||
|
||||
if __name__ == '__main__':
|
||||
import unittest
|
||||
suite = lambda: unittest.TestSuite(get_tests())
|
||||
unittest.main(defaultTest='suite')
|
||||
|
||||
# vim:set ts=4 sw=4 sts=4 expandtab:
|
339
modules/Crypto/SelfTest/Cipher/test_DES.py
Normal file
339
modules/Crypto/SelfTest/Cipher/test_DES.py
Normal file
@ -0,0 +1,339 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# SelfTest/Cipher/DES.py: Self-test for the (Single) DES cipher
|
||||
#
|
||||
# Written in 2008 by Dwayne C. Litzenberger <dlitz@dlitz.net>
|
||||
#
|
||||
# ===================================================================
|
||||
# The contents of this file are dedicated to the public domain. To
|
||||
# the extent that dedication to the public domain is not available,
|
||||
# everyone is granted a worldwide, perpetual, royalty-free,
|
||||
# non-exclusive license to exercise all rights associated with the
|
||||
# contents of this file for any purpose whatsoever.
|
||||
# No rights are reserved.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
# SOFTWARE.
|
||||
# ===================================================================
|
||||
|
||||
"""Self-test suite for Crypto.Cipher.DES"""
|
||||
|
||||
__revision__ = "$Id$"
|
||||
|
||||
from common import dict # For compatibility with Python 2.1 and 2.2
|
||||
from Crypto.Util.py3compat import *
|
||||
import unittest
|
||||
|
||||
# This is a list of (plaintext, ciphertext, key, description) tuples.
|
||||
SP800_17_B1_KEY = '01' * 8
|
||||
SP800_17_B2_PT = '00' * 8
|
||||
test_data = [
|
||||
# Test vectors from Appendix A of NIST SP 800-17
|
||||
# "Modes of Operation Validation System (MOVS): Requirements and Procedures"
|
||||
# http://csrc.nist.gov/publications/nistpubs/800-17/800-17.pdf
|
||||
|
||||
# Appendix A - "Sample Round Outputs for the DES"
|
||||
('0000000000000000', '82dcbafbdeab6602', '10316e028c8f3b4a',
|
||||
"NIST SP800-17 A"),
|
||||
|
||||
# Table B.1 - Variable Plaintext Known Answer Test
|
||||
('8000000000000000', '95f8a5e5dd31d900', SP800_17_B1_KEY,
|
||||
'NIST SP800-17 B.1 #0'),
|
||||
('4000000000000000', 'dd7f121ca5015619', SP800_17_B1_KEY,
|
||||
'NIST SP800-17 B.1 #1'),
|
||||
('2000000000000000', '2e8653104f3834ea', SP800_17_B1_KEY,
|
||||
'NIST SP800-17 B.1 #2'),
|
||||
('1000000000000000', '4bd388ff6cd81d4f', SP800_17_B1_KEY,
|
||||
'NIST SP800-17 B.1 #3'),
|
||||
('0800000000000000', '20b9e767b2fb1456', SP800_17_B1_KEY,
|
||||
'NIST SP800-17 B.1 #4'),
|
||||
('0400000000000000', '55579380d77138ef', SP800_17_B1_KEY,
|
||||
'NIST SP800-17 B.1 #5'),
|
||||
('0200000000000000', '6cc5defaaf04512f', SP800_17_B1_KEY,
|
||||
'NIST SP800-17 B.1 #6'),
|
||||
('0100000000000000', '0d9f279ba5d87260', SP800_17_B1_KEY,
|
||||
'NIST SP800-17 B.1 #7'),
|
||||
('0080000000000000', 'd9031b0271bd5a0a', SP800_17_B1_KEY,
|
||||
'NIST SP800-17 B.1 #8'),
|
||||
('0040000000000000', '424250b37c3dd951', SP800_17_B1_KEY,
|
||||
'NIST SP800-17 B.1 #9'),
|
||||
('0020000000000000', 'b8061b7ecd9a21e5', SP800_17_B1_KEY,
|
||||
'NIST SP800-17 B.1 #10'),
|
||||
('0010000000000000', 'f15d0f286b65bd28', SP800_17_B1_KEY,
|
||||
'NIST SP800-17 B.1 #11'),
|
||||
('0008000000000000', 'add0cc8d6e5deba1', SP800_17_B1_KEY,
|
||||
'NIST SP800-17 B.1 #12'),
|
||||
('0004000000000000', 'e6d5f82752ad63d1', SP800_17_B1_KEY,
|
||||
'NIST SP800-17 B.1 #13'),
|
||||
('0002000000000000', 'ecbfe3bd3f591a5e', SP800_17_B1_KEY,
|
||||
'NIST SP800-17 B.1 #14'),
|
||||
('0001000000000000', 'f356834379d165cd', SP800_17_B1_KEY,
|
||||
'NIST SP800-17 B.1 #15'),
|
||||
('0000800000000000', '2b9f982f20037fa9', SP800_17_B1_KEY,
|
||||
'NIST SP800-17 B.1 #16'),
|
||||
('0000400000000000', '889de068a16f0be6', SP800_17_B1_KEY,
|
||||
'NIST SP800-17 B.1 #17'),
|
||||
('0000200000000000', 'e19e275d846a1298', SP800_17_B1_KEY,
|
||||
'NIST SP800-17 B.1 #18'),
|
||||
('0000100000000000', '329a8ed523d71aec', SP800_17_B1_KEY,
|
||||
'NIST SP800-17 B.1 #19'),
|
||||
('0000080000000000', 'e7fce22557d23c97', SP800_17_B1_KEY,
|
||||
'NIST SP800-17 B.1 #20'),
|
||||
('0000040000000000', '12a9f5817ff2d65d', SP800_17_B1_KEY,
|
||||
'NIST SP800-17 B.1 #21'),
|
||||
('0000020000000000', 'a484c3ad38dc9c19', SP800_17_B1_KEY,
|
||||
'NIST SP800-17 B.1 #22'),
|
||||
('0000010000000000', 'fbe00a8a1ef8ad72', SP800_17_B1_KEY,
|
||||
'NIST SP800-17 B.1 #23'),
|
||||
('0000008000000000', '750d079407521363', SP800_17_B1_KEY,
|
||||
'NIST SP800-17 B.1 #24'),
|
||||
('0000004000000000', '64feed9c724c2faf', SP800_17_B1_KEY,
|
||||
'NIST SP800-17 B.1 #25'),
|
||||
('0000002000000000', 'f02b263b328e2b60', SP800_17_B1_KEY,
|
||||
'NIST SP800-17 B.1 #26'),
|
||||
('0000001000000000', '9d64555a9a10b852', SP800_17_B1_KEY,
|
||||
'NIST SP800-17 B.1 #27'),
|
||||
('0000000800000000', 'd106ff0bed5255d7', SP800_17_B1_KEY,
|
||||
'NIST SP800-17 B.1 #28'),
|
||||
('0000000400000000', 'e1652c6b138c64a5', SP800_17_B1_KEY,
|
||||
'NIST SP800-17 B.1 #29'),
|
||||
('0000000200000000', 'e428581186ec8f46', SP800_17_B1_KEY,
|
||||
'NIST SP800-17 B.1 #30'),
|
||||
('0000000100000000', 'aeb5f5ede22d1a36', SP800_17_B1_KEY,
|
||||
'NIST SP800-17 B.1 #31'),
|
||||
('0000000080000000', 'e943d7568aec0c5c', SP800_17_B1_KEY,
|
||||
'NIST SP800-17 B.1 #32'),
|
||||
('0000000040000000', 'df98c8276f54b04b', SP800_17_B1_KEY,
|
||||
'NIST SP800-17 B.1 #33'),
|
||||
('0000000020000000', 'b160e4680f6c696f', SP800_17_B1_KEY,
|
||||
'NIST SP800-17 B.1 #34'),
|
||||
('0000000010000000', 'fa0752b07d9c4ab8', SP800_17_B1_KEY,
|
||||
'NIST SP800-17 B.1 #35'),
|
||||
('0000000008000000', 'ca3a2b036dbc8502', SP800_17_B1_KEY,
|
||||
'NIST SP800-17 B.1 #36'),
|
||||
('0000000004000000', '5e0905517bb59bcf', SP800_17_B1_KEY,
|
||||
'NIST SP800-17 B.1 #37'),
|
||||
('0000000002000000', '814eeb3b91d90726', SP800_17_B1_KEY,
|
||||
'NIST SP800-17 B.1 #38'),
|
||||
('0000000001000000', '4d49db1532919c9f', SP800_17_B1_KEY,
|
||||
'NIST SP800-17 B.1 #39'),
|
||||
('0000000000800000', '25eb5fc3f8cf0621', SP800_17_B1_KEY,
|
||||
'NIST SP800-17 B.1 #40'),
|
||||
('0000000000400000', 'ab6a20c0620d1c6f', SP800_17_B1_KEY,
|
||||
'NIST SP800-17 B.1 #41'),
|
||||
('0000000000200000', '79e90dbc98f92cca', SP800_17_B1_KEY,
|
||||
'NIST SP800-17 B.1 #42'),
|
||||
('0000000000100000', '866ecedd8072bb0e', SP800_17_B1_KEY,
|
||||
'NIST SP800-17 B.1 #43'),
|
||||
('0000000000080000', '8b54536f2f3e64a8', SP800_17_B1_KEY,
|
||||
'NIST SP800-17 B.1 #44'),
|
||||
('0000000000040000', 'ea51d3975595b86b', SP800_17_B1_KEY,
|
||||
'NIST SP800-17 B.1 #45'),
|
||||
('0000000000020000', 'caffc6ac4542de31', SP800_17_B1_KEY,
|
||||
'NIST SP800-17 B.1 #46'),
|
||||
('0000000000010000', '8dd45a2ddf90796c', SP800_17_B1_KEY,
|
||||
'NIST SP800-17 B.1 #47'),
|
||||
('0000000000008000', '1029d55e880ec2d0', SP800_17_B1_KEY,
|
||||
'NIST SP800-17 B.1 #48'),
|
||||
('0000000000004000', '5d86cb23639dbea9', SP800_17_B1_KEY,
|
||||
'NIST SP800-17 B.1 #49'),
|
||||
('0000000000002000', '1d1ca853ae7c0c5f', SP800_17_B1_KEY,
|
||||
'NIST SP800-17 B.1 #50'),
|
||||
('0000000000001000', 'ce332329248f3228', SP800_17_B1_KEY,
|
||||
'NIST SP800-17 B.1 #51'),
|
||||
('0000000000000800', '8405d1abe24fb942', SP800_17_B1_KEY,
|
||||
'NIST SP800-17 B.1 #52'),
|
||||
('0000000000000400', 'e643d78090ca4207', SP800_17_B1_KEY,
|
||||
'NIST SP800-17 B.1 #53'),
|
||||
('0000000000000200', '48221b9937748a23', SP800_17_B1_KEY,
|
||||
'NIST SP800-17 B.1 #54'),
|
||||
('0000000000000100', 'dd7c0bbd61fafd54', SP800_17_B1_KEY,
|
||||
'NIST SP800-17 B.1 #55'),
|
||||
('0000000000000080', '2fbc291a570db5c4', SP800_17_B1_KEY,
|
||||
'NIST SP800-17 B.1 #56'),
|
||||
('0000000000000040', 'e07c30d7e4e26e12', SP800_17_B1_KEY,
|
||||
'NIST SP800-17 B.1 #57'),
|
||||
('0000000000000020', '0953e2258e8e90a1', SP800_17_B1_KEY,
|
||||
'NIST SP800-17 B.1 #58'),
|
||||
('0000000000000010', '5b711bc4ceebf2ee', SP800_17_B1_KEY,
|
||||
'NIST SP800-17 B.1 #59'),
|
||||
('0000000000000008', 'cc083f1e6d9e85f6', SP800_17_B1_KEY,
|
||||
'NIST SP800-17 B.1 #60'),
|
||||
('0000000000000004', 'd2fd8867d50d2dfe', SP800_17_B1_KEY,
|
||||
'NIST SP800-17 B.1 #61'),
|
||||
('0000000000000002', '06e7ea22ce92708f', SP800_17_B1_KEY,
|
||||
'NIST SP800-17 B.1 #62'),
|
||||
('0000000000000001', '166b40b44aba4bd6', SP800_17_B1_KEY,
|
||||
'NIST SP800-17 B.1 #63'),
|
||||
|
||||
# Table B.2 - Variable Key Known Answer Test
|
||||
(SP800_17_B2_PT, '95a8d72813daa94d', '8001010101010101',
|
||||
'NIST SP800-17 B.2 #0'),
|
||||
(SP800_17_B2_PT, '0eec1487dd8c26d5', '4001010101010101',
|
||||
'NIST SP800-17 B.2 #1'),
|
||||
(SP800_17_B2_PT, '7ad16ffb79c45926', '2001010101010101',
|
||||
'NIST SP800-17 B.2 #2'),
|
||||
(SP800_17_B2_PT, 'd3746294ca6a6cf3', '1001010101010101',
|
||||
'NIST SP800-17 B.2 #3'),
|
||||
(SP800_17_B2_PT, '809f5f873c1fd761', '0801010101010101',
|
||||
'NIST SP800-17 B.2 #4'),
|
||||
(SP800_17_B2_PT, 'c02faffec989d1fc', '0401010101010101',
|
||||
'NIST SP800-17 B.2 #5'),
|
||||
(SP800_17_B2_PT, '4615aa1d33e72f10', '0201010101010101',
|
||||
'NIST SP800-17 B.2 #6'),
|
||||
(SP800_17_B2_PT, '2055123350c00858', '0180010101010101',
|
||||
'NIST SP800-17 B.2 #7'),
|
||||
(SP800_17_B2_PT, 'df3b99d6577397c8', '0140010101010101',
|
||||
'NIST SP800-17 B.2 #8'),
|
||||
(SP800_17_B2_PT, '31fe17369b5288c9', '0120010101010101',
|
||||
'NIST SP800-17 B.2 #9'),
|
||||
(SP800_17_B2_PT, 'dfdd3cc64dae1642', '0110010101010101',
|
||||
'NIST SP800-17 B.2 #10'),
|
||||
(SP800_17_B2_PT, '178c83ce2b399d94', '0108010101010101',
|
||||
'NIST SP800-17 B.2 #11'),
|
||||
(SP800_17_B2_PT, '50f636324a9b7f80', '0104010101010101',
|
||||
'NIST SP800-17 B.2 #12'),
|
||||
(SP800_17_B2_PT, 'a8468ee3bc18f06d', '0102010101010101',
|
||||
'NIST SP800-17 B.2 #13'),
|
||||
(SP800_17_B2_PT, 'a2dc9e92fd3cde92', '0101800101010101',
|
||||
'NIST SP800-17 B.2 #14'),
|
||||
(SP800_17_B2_PT, 'cac09f797d031287', '0101400101010101',
|
||||
'NIST SP800-17 B.2 #15'),
|
||||
(SP800_17_B2_PT, '90ba680b22aeb525', '0101200101010101',
|
||||
'NIST SP800-17 B.2 #16'),
|
||||
(SP800_17_B2_PT, 'ce7a24f350e280b6', '0101100101010101',
|
||||
'NIST SP800-17 B.2 #17'),
|
||||
(SP800_17_B2_PT, '882bff0aa01a0b87', '0101080101010101',
|
||||
'NIST SP800-17 B.2 #18'),
|
||||
(SP800_17_B2_PT, '25610288924511c2', '0101040101010101',
|
||||
'NIST SP800-17 B.2 #19'),
|
||||
(SP800_17_B2_PT, 'c71516c29c75d170', '0101020101010101',
|
||||
'NIST SP800-17 B.2 #20'),
|
||||
(SP800_17_B2_PT, '5199c29a52c9f059', '0101018001010101',
|
||||
'NIST SP800-17 B.2 #21'),
|
||||
(SP800_17_B2_PT, 'c22f0a294a71f29f', '0101014001010101',
|
||||
'NIST SP800-17 B.2 #22'),
|
||||
(SP800_17_B2_PT, 'ee371483714c02ea', '0101012001010101',
|
||||
'NIST SP800-17 B.2 #23'),
|
||||
(SP800_17_B2_PT, 'a81fbd448f9e522f', '0101011001010101',
|
||||
'NIST SP800-17 B.2 #24'),
|
||||
(SP800_17_B2_PT, '4f644c92e192dfed', '0101010801010101',
|
||||
'NIST SP800-17 B.2 #25'),
|
||||
(SP800_17_B2_PT, '1afa9a66a6df92ae', '0101010401010101',
|
||||
'NIST SP800-17 B.2 #26'),
|
||||
(SP800_17_B2_PT, 'b3c1cc715cb879d8', '0101010201010101',
|
||||
'NIST SP800-17 B.2 #27'),
|
||||
(SP800_17_B2_PT, '19d032e64ab0bd8b', '0101010180010101',
|
||||
'NIST SP800-17 B.2 #28'),
|
||||
(SP800_17_B2_PT, '3cfaa7a7dc8720dc', '0101010140010101',
|
||||
'NIST SP800-17 B.2 #29'),
|
||||
(SP800_17_B2_PT, 'b7265f7f447ac6f3', '0101010120010101',
|
||||
'NIST SP800-17 B.2 #30'),
|
||||
(SP800_17_B2_PT, '9db73b3c0d163f54', '0101010110010101',
|
||||
'NIST SP800-17 B.2 #31'),
|
||||
(SP800_17_B2_PT, '8181b65babf4a975', '0101010108010101',
|
||||
'NIST SP800-17 B.2 #32'),
|
||||
(SP800_17_B2_PT, '93c9b64042eaa240', '0101010104010101',
|
||||
'NIST SP800-17 B.2 #33'),
|
||||
(SP800_17_B2_PT, '5570530829705592', '0101010102010101',
|
||||
'NIST SP800-17 B.2 #34'),
|
||||
(SP800_17_B2_PT, '8638809e878787a0', '0101010101800101',
|
||||
'NIST SP800-17 B.2 #35'),
|
||||
(SP800_17_B2_PT, '41b9a79af79ac208', '0101010101400101',
|
||||
'NIST SP800-17 B.2 #36'),
|
||||
(SP800_17_B2_PT, '7a9be42f2009a892', '0101010101200101',
|
||||
'NIST SP800-17 B.2 #37'),
|
||||
(SP800_17_B2_PT, '29038d56ba6d2745', '0101010101100101',
|
||||
'NIST SP800-17 B.2 #38'),
|
||||
(SP800_17_B2_PT, '5495c6abf1e5df51', '0101010101080101',
|
||||
'NIST SP800-17 B.2 #39'),
|
||||
(SP800_17_B2_PT, 'ae13dbd561488933', '0101010101040101',
|
||||
'NIST SP800-17 B.2 #40'),
|
||||
(SP800_17_B2_PT, '024d1ffa8904e389', '0101010101020101',
|
||||
'NIST SP800-17 B.2 #41'),
|
||||
(SP800_17_B2_PT, 'd1399712f99bf02e', '0101010101018001',
|
||||
'NIST SP800-17 B.2 #42'),
|
||||
(SP800_17_B2_PT, '14c1d7c1cffec79e', '0101010101014001',
|
||||
'NIST SP800-17 B.2 #43'),
|
||||
(SP800_17_B2_PT, '1de5279dae3bed6f', '0101010101012001',
|
||||
'NIST SP800-17 B.2 #44'),
|
||||
(SP800_17_B2_PT, 'e941a33f85501303', '0101010101011001',
|
||||
'NIST SP800-17 B.2 #45'),
|
||||
(SP800_17_B2_PT, 'da99dbbc9a03f379', '0101010101010801',
|
||||
'NIST SP800-17 B.2 #46'),
|
||||
(SP800_17_B2_PT, 'b7fc92f91d8e92e9', '0101010101010401',
|
||||
'NIST SP800-17 B.2 #47'),
|
||||
(SP800_17_B2_PT, 'ae8e5caa3ca04e85', '0101010101010201',
|
||||
'NIST SP800-17 B.2 #48'),
|
||||
(SP800_17_B2_PT, '9cc62df43b6eed74', '0101010101010180',
|
||||
'NIST SP800-17 B.2 #49'),
|
||||
(SP800_17_B2_PT, 'd863dbb5c59a91a0', '0101010101010140',
|
||||
'NIST SP800-17 B.2 #50'),
|
||||
(SP800_17_B2_PT, 'a1ab2190545b91d7', '0101010101010120',
|
||||
'NIST SP800-17 B.2 #51'),
|
||||
(SP800_17_B2_PT, '0875041e64c570f7', '0101010101010110',
|
||||
'NIST SP800-17 B.2 #52'),
|
||||
(SP800_17_B2_PT, '5a594528bebef1cc', '0101010101010108',
|
||||
'NIST SP800-17 B.2 #53'),
|
||||
(SP800_17_B2_PT, 'fcdb3291de21f0c0', '0101010101010104',
|
||||
'NIST SP800-17 B.2 #54'),
|
||||
(SP800_17_B2_PT, '869efd7f9f265a09', '0101010101010102',
|
||||
'NIST SP800-17 B.2 #55'),
|
||||
]
|
||||
|
||||
class RonRivestTest(unittest.TestCase):
|
||||
""" Ronald L. Rivest's DES test, see
|
||||
http://people.csail.mit.edu/rivest/Destest.txt
|
||||
ABSTRACT
|
||||
--------
|
||||
|
||||
We present a simple way to test the correctness of a DES implementation:
|
||||
Use the recurrence relation:
|
||||
|
||||
X0 = 9474B8E8C73BCA7D (hexadecimal)
|
||||
|
||||
X(i+1) = IF (i is even) THEN E(Xi,Xi) ELSE D(Xi,Xi)
|
||||
|
||||
to compute a sequence of 64-bit values: X0, X1, X2, ..., X16. Here
|
||||
E(X,K) denotes the DES encryption of X using key K, and D(X,K) denotes
|
||||
the DES decryption of X using key K. If you obtain
|
||||
|
||||
X16 = 1B1A2DDB4C642438
|
||||
|
||||
your implementation does not have any of the 36,568 possible single-fault
|
||||
errors described herein.
|
||||
"""
|
||||
def runTest(self):
|
||||
from Crypto.Cipher import DES
|
||||
from binascii import b2a_hex
|
||||
|
||||
X = []
|
||||
X[0:] = [b('\x94\x74\xB8\xE8\xC7\x3B\xCA\x7D')]
|
||||
|
||||
for i in range(16):
|
||||
c = DES.new(X[i],DES.MODE_ECB)
|
||||
if not (i&1): # (num&1) returns 1 for odd numbers
|
||||
X[i+1:] = [c.encrypt(X[i])] # even
|
||||
else:
|
||||
X[i+1:] = [c.decrypt(X[i])] # odd
|
||||
|
||||
self.assertEqual(b2a_hex(X[16]),
|
||||
b2a_hex(b('\x1B\x1A\x2D\xDB\x4C\x64\x24\x38')))
|
||||
|
||||
def get_tests(config={}):
|
||||
from Crypto.Cipher import DES
|
||||
from common import make_block_tests
|
||||
return make_block_tests(DES, "DES", test_data) + [RonRivestTest()]
|
||||
|
||||
if __name__ == '__main__':
|
||||
import unittest
|
||||
suite = lambda: unittest.TestSuite(get_tests())
|
||||
unittest.main(defaultTest='suite')
|
||||
|
||||
# vim:set ts=4 sw=4 sts=4 expandtab:
|
333
modules/Crypto/SelfTest/Cipher/test_DES3.py
Normal file
333
modules/Crypto/SelfTest/Cipher/test_DES3.py
Normal file
@ -0,0 +1,333 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# SelfTest/Cipher/DES3.py: Self-test for the Triple-DES cipher
|
||||
#
|
||||
# Written in 2008 by Dwayne C. Litzenberger <dlitz@dlitz.net>
|
||||
#
|
||||
# ===================================================================
|
||||
# The contents of this file are dedicated to the public domain. To
|
||||
# the extent that dedication to the public domain is not available,
|
||||
# everyone is granted a worldwide, perpetual, royalty-free,
|
||||
# non-exclusive license to exercise all rights associated with the
|
||||
# contents of this file for any purpose whatsoever.
|
||||
# No rights are reserved.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
# SOFTWARE.
|
||||
# ===================================================================
|
||||
|
||||
"""Self-test suite for Crypto.Cipher.DES3"""
|
||||
|
||||
__revision__ = "$Id$"
|
||||
|
||||
from common import dict # For compatibility with Python 2.1 and 2.2
|
||||
from Crypto.Util.py3compat import *
|
||||
from binascii import hexlify
|
||||
|
||||
# This is a list of (plaintext, ciphertext, key, description) tuples.
|
||||
SP800_20_A1_KEY = '01' * 24
|
||||
SP800_20_A2_PT = '00' * 8
|
||||
test_data = [
|
||||
# Test vector from Appendix B of NIST SP 800-67
|
||||
# "Recommendation for the Triple Data Encryption Algorithm (TDEA) Block
|
||||
# Cipher"
|
||||
# http://csrc.nist.gov/publications/nistpubs/800-67/SP800-67.pdf
|
||||
('54686520717566636b2062726f776e20666f78206a756d70',
|
||||
'a826fd8ce53b855fcce21c8112256fe668d5c05dd9b6b900',
|
||||
'0123456789abcdef23456789abcdef01456789abcdef0123',
|
||||
'NIST SP800-67 B.1'),
|
||||
|
||||
# Test vectors "The Multi-block Message Test (MMT) for DES and TDES"
|
||||
# http://csrc.nist.gov/groups/STM/cavp/documents/des/DESMMT.pdf
|
||||
('326a494cd33fe756', 'b22b8d66de970692',
|
||||
'627f460e08104a1043cd265d5840eaf1313edf97df2a8a8c',
|
||||
'DESMMT #1', dict(mode='CBC', iv='8e29f75ea77e5475')),
|
||||
|
||||
('84401f78fe6c10876d8ea23094ea5309', '7b1f7c7e3b1c948ebd04a75ffba7d2f5',
|
||||
'37ae5ebf46dff2dc0754b94f31cbb3855e7fd36dc870bfae',
|
||||
'DESMMT #2', dict(mode='CBC', iv='3d1de3cc132e3b65')),
|
||||
|
||||
# Test vectors from Appendix A of NIST SP 800-20
|
||||
# "Modes of Operation Validation System for the Triple Data Encryption
|
||||
# Algorithm (TMOVS): Requirements and Procedures"
|
||||
# http://csrc.nist.gov/publications/nistpubs/800-20/800-20.pdf
|
||||
|
||||
# Table A.1 - Variable Plaintext Known Answer Test
|
||||
('8000000000000000', '95f8a5e5dd31d900', SP800_20_A1_KEY,
|
||||
'NIST SP800-20 A.1 #0'),
|
||||
('4000000000000000', 'dd7f121ca5015619', SP800_20_A1_KEY,
|
||||
'NIST SP800-20 A.1 #1'),
|
||||
('2000000000000000', '2e8653104f3834ea', SP800_20_A1_KEY,
|
||||
'NIST SP800-20 A.1 #2'),
|
||||
('1000000000000000', '4bd388ff6cd81d4f', SP800_20_A1_KEY,
|
||||
'NIST SP800-20 A.1 #3'),
|
||||
('0800000000000000', '20b9e767b2fb1456', SP800_20_A1_KEY,
|
||||
'NIST SP800-20 A.1 #4'),
|
||||
('0400000000000000', '55579380d77138ef', SP800_20_A1_KEY,
|
||||
'NIST SP800-20 A.1 #5'),
|
||||
('0200000000000000', '6cc5defaaf04512f', SP800_20_A1_KEY,
|
||||
'NIST SP800-20 A.1 #6'),
|
||||
('0100000000000000', '0d9f279ba5d87260', SP800_20_A1_KEY,
|
||||
'NIST SP800-20 A.1 #7'),
|
||||
('0080000000000000', 'd9031b0271bd5a0a', SP800_20_A1_KEY,
|
||||
'NIST SP800-20 A.1 #8'),
|
||||
('0040000000000000', '424250b37c3dd951', SP800_20_A1_KEY,
|
||||
'NIST SP800-20 A.1 #9'),
|
||||
('0020000000000000', 'b8061b7ecd9a21e5', SP800_20_A1_KEY,
|
||||
'NIST SP800-20 A.1 #10'),
|
||||
('0010000000000000', 'f15d0f286b65bd28', SP800_20_A1_KEY,
|
||||
'NIST SP800-20 A.1 #11'),
|
||||
('0008000000000000', 'add0cc8d6e5deba1', SP800_20_A1_KEY,
|
||||
'NIST SP800-20 A.1 #12'),
|
||||
('0004000000000000', 'e6d5f82752ad63d1', SP800_20_A1_KEY,
|
||||
'NIST SP800-20 A.1 #13'),
|
||||
('0002000000000000', 'ecbfe3bd3f591a5e', SP800_20_A1_KEY,
|
||||
'NIST SP800-20 A.1 #14'),
|
||||
('0001000000000000', 'f356834379d165cd', SP800_20_A1_KEY,
|
||||
'NIST SP800-20 A.1 #15'),
|
||||
('0000800000000000', '2b9f982f20037fa9', SP800_20_A1_KEY,
|
||||
'NIST SP800-20 A.1 #16'),
|
||||
('0000400000000000', '889de068a16f0be6', SP800_20_A1_KEY,
|
||||
'NIST SP800-20 A.1 #17'),
|
||||
('0000200000000000', 'e19e275d846a1298', SP800_20_A1_KEY,
|
||||
'NIST SP800-20 A.1 #18'),
|
||||
('0000100000000000', '329a8ed523d71aec', SP800_20_A1_KEY,
|
||||
'NIST SP800-20 A.1 #19'),
|
||||
('0000080000000000', 'e7fce22557d23c97', SP800_20_A1_KEY,
|
||||
'NIST SP800-20 A.1 #20'),
|
||||
('0000040000000000', '12a9f5817ff2d65d', SP800_20_A1_KEY,
|
||||
'NIST SP800-20 A.1 #21'),
|
||||
('0000020000000000', 'a484c3ad38dc9c19', SP800_20_A1_KEY,
|
||||
'NIST SP800-20 A.1 #22'),
|
||||
('0000010000000000', 'fbe00a8a1ef8ad72', SP800_20_A1_KEY,
|
||||
'NIST SP800-20 A.1 #23'),
|
||||
('0000008000000000', '750d079407521363', SP800_20_A1_KEY,
|
||||
'NIST SP800-20 A.1 #24'),
|
||||
('0000004000000000', '64feed9c724c2faf', SP800_20_A1_KEY,
|
||||
'NIST SP800-20 A.1 #25'),
|
||||
('0000002000000000', 'f02b263b328e2b60', SP800_20_A1_KEY,
|
||||
'NIST SP800-20 A.1 #26'),
|
||||
('0000001000000000', '9d64555a9a10b852', SP800_20_A1_KEY,
|
||||
'NIST SP800-20 A.1 #27'),
|
||||
('0000000800000000', 'd106ff0bed5255d7', SP800_20_A1_KEY,
|
||||
'NIST SP800-20 A.1 #28'),
|
||||
('0000000400000000', 'e1652c6b138c64a5', SP800_20_A1_KEY,
|
||||
'NIST SP800-20 A.1 #29'),
|
||||
('0000000200000000', 'e428581186ec8f46', SP800_20_A1_KEY,
|
||||
'NIST SP800-20 A.1 #30'),
|
||||
('0000000100000000', 'aeb5f5ede22d1a36', SP800_20_A1_KEY,
|
||||
'NIST SP800-20 A.1 #31'),
|
||||
('0000000080000000', 'e943d7568aec0c5c', SP800_20_A1_KEY,
|
||||
'NIST SP800-20 A.1 #32'),
|
||||
('0000000040000000', 'df98c8276f54b04b', SP800_20_A1_KEY,
|
||||
'NIST SP800-20 A.1 #33'),
|
||||
('0000000020000000', 'b160e4680f6c696f', SP800_20_A1_KEY,
|
||||
'NIST SP800-20 A.1 #34'),
|
||||
('0000000010000000', 'fa0752b07d9c4ab8', SP800_20_A1_KEY,
|
||||
'NIST SP800-20 A.1 #35'),
|
||||
('0000000008000000', 'ca3a2b036dbc8502', SP800_20_A1_KEY,
|
||||
'NIST SP800-20 A.1 #36'),
|
||||
('0000000004000000', '5e0905517bb59bcf', SP800_20_A1_KEY,
|
||||
'NIST SP800-20 A.1 #37'),
|
||||
('0000000002000000', '814eeb3b91d90726', SP800_20_A1_KEY,
|
||||
'NIST SP800-20 A.1 #38'),
|
||||
('0000000001000000', '4d49db1532919c9f', SP800_20_A1_KEY,
|
||||
'NIST SP800-20 A.1 #39'),
|
||||
('0000000000800000', '25eb5fc3f8cf0621', SP800_20_A1_KEY,
|
||||
'NIST SP800-20 A.1 #40'),
|
||||
('0000000000400000', 'ab6a20c0620d1c6f', SP800_20_A1_KEY,
|
||||
'NIST SP800-20 A.1 #41'),
|
||||
('0000000000200000', '79e90dbc98f92cca', SP800_20_A1_KEY,
|
||||
'NIST SP800-20 A.1 #42'),
|
||||
('0000000000100000', '866ecedd8072bb0e', SP800_20_A1_KEY,
|
||||
'NIST SP800-20 A.1 #43'),
|
||||
('0000000000080000', '8b54536f2f3e64a8', SP800_20_A1_KEY,
|
||||
'NIST SP800-20 A.1 #44'),
|
||||
('0000000000040000', 'ea51d3975595b86b', SP800_20_A1_KEY,
|
||||
'NIST SP800-20 A.1 #45'),
|
||||
('0000000000020000', 'caffc6ac4542de31', SP800_20_A1_KEY,
|
||||
'NIST SP800-20 A.1 #46'),
|
||||
('0000000000010000', '8dd45a2ddf90796c', SP800_20_A1_KEY,
|
||||
'NIST SP800-20 A.1 #47'),
|
||||
('0000000000008000', '1029d55e880ec2d0', SP800_20_A1_KEY,
|
||||
'NIST SP800-20 A.1 #48'),
|
||||
('0000000000004000', '5d86cb23639dbea9', SP800_20_A1_KEY,
|
||||
'NIST SP800-20 A.1 #49'),
|
||||
('0000000000002000', '1d1ca853ae7c0c5f', SP800_20_A1_KEY,
|
||||
'NIST SP800-20 A.1 #50'),
|
||||
('0000000000001000', 'ce332329248f3228', SP800_20_A1_KEY,
|
||||
'NIST SP800-20 A.1 #51'),
|
||||
('0000000000000800', '8405d1abe24fb942', SP800_20_A1_KEY,
|
||||
'NIST SP800-20 A.1 #52'),
|
||||
('0000000000000400', 'e643d78090ca4207', SP800_20_A1_KEY,
|
||||
'NIST SP800-20 A.1 #53'),
|
||||
('0000000000000200', '48221b9937748a23', SP800_20_A1_KEY,
|
||||
'NIST SP800-20 A.1 #54'),
|
||||
('0000000000000100', 'dd7c0bbd61fafd54', SP800_20_A1_KEY,
|
||||
'NIST SP800-20 A.1 #55'),
|
||||
('0000000000000080', '2fbc291a570db5c4', SP800_20_A1_KEY,
|
||||
'NIST SP800-20 A.1 #56'),
|
||||
('0000000000000040', 'e07c30d7e4e26e12', SP800_20_A1_KEY,
|
||||
'NIST SP800-20 A.1 #57'),
|
||||
('0000000000000020', '0953e2258e8e90a1', SP800_20_A1_KEY,
|
||||
'NIST SP800-20 A.1 #58'),
|
||||
('0000000000000010', '5b711bc4ceebf2ee', SP800_20_A1_KEY,
|
||||
'NIST SP800-20 A.1 #59'),
|
||||
('0000000000000008', 'cc083f1e6d9e85f6', SP800_20_A1_KEY,
|
||||
'NIST SP800-20 A.1 #60'),
|
||||
('0000000000000004', 'd2fd8867d50d2dfe', SP800_20_A1_KEY,
|
||||
'NIST SP800-20 A.1 #61'),
|
||||
('0000000000000002', '06e7ea22ce92708f', SP800_20_A1_KEY,
|
||||
'NIST SP800-20 A.1 #62'),
|
||||
('0000000000000001', '166b40b44aba4bd6', SP800_20_A1_KEY,
|
||||
'NIST SP800-20 A.1 #63'),
|
||||
|
||||
# Table A.2 - Variable Key Known Answer Test
|
||||
(SP800_20_A2_PT, '95a8d72813daa94d', '8001010101010101'*3,
|
||||
'NIST SP800-20 A.2 #0'),
|
||||
(SP800_20_A2_PT, '0eec1487dd8c26d5', '4001010101010101'*3,
|
||||
'NIST SP800-20 A.2 #1'),
|
||||
(SP800_20_A2_PT, '7ad16ffb79c45926', '2001010101010101'*3,
|
||||
'NIST SP800-20 A.2 #2'),
|
||||
(SP800_20_A2_PT, 'd3746294ca6a6cf3', '1001010101010101'*3,
|
||||
'NIST SP800-20 A.2 #3'),
|
||||
(SP800_20_A2_PT, '809f5f873c1fd761', '0801010101010101'*3,
|
||||
'NIST SP800-20 A.2 #4'),
|
||||
(SP800_20_A2_PT, 'c02faffec989d1fc', '0401010101010101'*3,
|
||||
'NIST SP800-20 A.2 #5'),
|
||||
(SP800_20_A2_PT, '4615aa1d33e72f10', '0201010101010101'*3,
|
||||
'NIST SP800-20 A.2 #6'),
|
||||
(SP800_20_A2_PT, '2055123350c00858', '0180010101010101'*3,
|
||||
'NIST SP800-20 A.2 #7'),
|
||||
(SP800_20_A2_PT, 'df3b99d6577397c8', '0140010101010101'*3,
|
||||
'NIST SP800-20 A.2 #8'),
|
||||
(SP800_20_A2_PT, '31fe17369b5288c9', '0120010101010101'*3,
|
||||
'NIST SP800-20 A.2 #9'),
|
||||
(SP800_20_A2_PT, 'dfdd3cc64dae1642', '0110010101010101'*3,
|
||||
'NIST SP800-20 A.2 #10'),
|
||||
(SP800_20_A2_PT, '178c83ce2b399d94', '0108010101010101'*3,
|
||||
'NIST SP800-20 A.2 #11'),
|
||||
(SP800_20_A2_PT, '50f636324a9b7f80', '0104010101010101'*3,
|
||||
'NIST SP800-20 A.2 #12'),
|
||||
(SP800_20_A2_PT, 'a8468ee3bc18f06d', '0102010101010101'*3,
|
||||
'NIST SP800-20 A.2 #13'),
|
||||
(SP800_20_A2_PT, 'a2dc9e92fd3cde92', '0101800101010101'*3,
|
||||
'NIST SP800-20 A.2 #14'),
|
||||
(SP800_20_A2_PT, 'cac09f797d031287', '0101400101010101'*3,
|
||||
'NIST SP800-20 A.2 #15'),
|
||||
(SP800_20_A2_PT, '90ba680b22aeb525', '0101200101010101'*3,
|
||||
'NIST SP800-20 A.2 #16'),
|
||||
(SP800_20_A2_PT, 'ce7a24f350e280b6', '0101100101010101'*3,
|
||||
'NIST SP800-20 A.2 #17'),
|
||||
(SP800_20_A2_PT, '882bff0aa01a0b87', '0101080101010101'*3,
|
||||
'NIST SP800-20 A.2 #18'),
|
||||
(SP800_20_A2_PT, '25610288924511c2', '0101040101010101'*3,
|
||||
'NIST SP800-20 A.2 #19'),
|
||||
(SP800_20_A2_PT, 'c71516c29c75d170', '0101020101010101'*3,
|
||||
'NIST SP800-20 A.2 #20'),
|
||||
(SP800_20_A2_PT, '5199c29a52c9f059', '0101018001010101'*3,
|
||||
'NIST SP800-20 A.2 #21'),
|
||||
(SP800_20_A2_PT, 'c22f0a294a71f29f', '0101014001010101'*3,
|
||||
'NIST SP800-20 A.2 #22'),
|
||||
(SP800_20_A2_PT, 'ee371483714c02ea', '0101012001010101'*3,
|
||||
'NIST SP800-20 A.2 #23'),
|
||||
(SP800_20_A2_PT, 'a81fbd448f9e522f', '0101011001010101'*3,
|
||||
'NIST SP800-20 A.2 #24'),
|
||||
(SP800_20_A2_PT, '4f644c92e192dfed', '0101010801010101'*3,
|
||||
'NIST SP800-20 A.2 #25'),
|
||||
(SP800_20_A2_PT, '1afa9a66a6df92ae', '0101010401010101'*3,
|
||||
'NIST SP800-20 A.2 #26'),
|
||||
(SP800_20_A2_PT, 'b3c1cc715cb879d8', '0101010201010101'*3,
|
||||
'NIST SP800-20 A.2 #27'),
|
||||
(SP800_20_A2_PT, '19d032e64ab0bd8b', '0101010180010101'*3,
|
||||
'NIST SP800-20 A.2 #28'),
|
||||
(SP800_20_A2_PT, '3cfaa7a7dc8720dc', '0101010140010101'*3,
|
||||
'NIST SP800-20 A.2 #29'),
|
||||
(SP800_20_A2_PT, 'b7265f7f447ac6f3', '0101010120010101'*3,
|
||||
'NIST SP800-20 A.2 #30'),
|
||||
(SP800_20_A2_PT, '9db73b3c0d163f54', '0101010110010101'*3,
|
||||
'NIST SP800-20 A.2 #31'),
|
||||
(SP800_20_A2_PT, '8181b65babf4a975', '0101010108010101'*3,
|
||||
'NIST SP800-20 A.2 #32'),
|
||||
(SP800_20_A2_PT, '93c9b64042eaa240', '0101010104010101'*3,
|
||||
'NIST SP800-20 A.2 #33'),
|
||||
(SP800_20_A2_PT, '5570530829705592', '0101010102010101'*3,
|
||||
'NIST SP800-20 A.2 #34'),
|
||||
(SP800_20_A2_PT, '8638809e878787a0', '0101010101800101'*3,
|
||||
'NIST SP800-20 A.2 #35'),
|
||||
(SP800_20_A2_PT, '41b9a79af79ac208', '0101010101400101'*3,
|
||||
'NIST SP800-20 A.2 #36'),
|
||||
(SP800_20_A2_PT, '7a9be42f2009a892', '0101010101200101'*3,
|
||||
'NIST SP800-20 A.2 #37'),
|
||||
(SP800_20_A2_PT, '29038d56ba6d2745', '0101010101100101'*3,
|
||||
'NIST SP800-20 A.2 #38'),
|
||||
(SP800_20_A2_PT, '5495c6abf1e5df51', '0101010101080101'*3,
|
||||
'NIST SP800-20 A.2 #39'),
|
||||
(SP800_20_A2_PT, 'ae13dbd561488933', '0101010101040101'*3,
|
||||
'NIST SP800-20 A.2 #40'),
|
||||
(SP800_20_A2_PT, '024d1ffa8904e389', '0101010101020101'*3,
|
||||
'NIST SP800-20 A.2 #41'),
|
||||
(SP800_20_A2_PT, 'd1399712f99bf02e', '0101010101018001'*3,
|
||||
'NIST SP800-20 A.2 #42'),
|
||||
(SP800_20_A2_PT, '14c1d7c1cffec79e', '0101010101014001'*3,
|
||||
'NIST SP800-20 A.2 #43'),
|
||||
(SP800_20_A2_PT, '1de5279dae3bed6f', '0101010101012001'*3,
|
||||
'NIST SP800-20 A.2 #44'),
|
||||
(SP800_20_A2_PT, 'e941a33f85501303', '0101010101011001'*3,
|
||||
'NIST SP800-20 A.2 #45'),
|
||||
(SP800_20_A2_PT, 'da99dbbc9a03f379', '0101010101010801'*3,
|
||||
'NIST SP800-20 A.2 #46'),
|
||||
(SP800_20_A2_PT, 'b7fc92f91d8e92e9', '0101010101010401'*3,
|
||||
'NIST SP800-20 A.2 #47'),
|
||||
(SP800_20_A2_PT, 'ae8e5caa3ca04e85', '0101010101010201'*3,
|
||||
'NIST SP800-20 A.2 #48'),
|
||||
(SP800_20_A2_PT, '9cc62df43b6eed74', '0101010101010180'*3,
|
||||
'NIST SP800-20 A.2 #49'),
|
||||
(SP800_20_A2_PT, 'd863dbb5c59a91a0', '0101010101010140'*3,
|
||||
'NIST SP800-20 A.2 #50'),
|
||||
(SP800_20_A2_PT, 'a1ab2190545b91d7', '0101010101010120'*3,
|
||||
'NIST SP800-20 A.2 #51'),
|
||||
(SP800_20_A2_PT, '0875041e64c570f7', '0101010101010110'*3,
|
||||
'NIST SP800-20 A.2 #52'),
|
||||
(SP800_20_A2_PT, '5a594528bebef1cc', '0101010101010108'*3,
|
||||
'NIST SP800-20 A.2 #53'),
|
||||
(SP800_20_A2_PT, 'fcdb3291de21f0c0', '0101010101010104'*3,
|
||||
'NIST SP800-20 A.2 #54'),
|
||||
(SP800_20_A2_PT, '869efd7f9f265a09', '0101010101010102'*3,
|
||||
'NIST SP800-20 A.2 #55'),
|
||||
|
||||
# "Two-key 3DES". Test vector generated using PyCrypto 2.0.1.
|
||||
# This test is designed to test the DES3 API, not the correctness of the
|
||||
# output.
|
||||
('21e81b7ade88a259', '5c577d4d9b20c0f8',
|
||||
'9b397ebf81b1181e282f4bb8adbadc6b', 'Two-key 3DES'),
|
||||
|
||||
# The following test vectors have been generated with gpg v1.4.0.
|
||||
# The command line used was:
|
||||
# gpg -c -z 0 --cipher-algo 3DES --passphrase secret_passphrase \
|
||||
# --disable-mdc --s2k-mode 0 --output ct pt
|
||||
# For an explanation, see test_AES.py .
|
||||
( 'ac1762037074324fb53ba3596f73656d69746556616c6c6579', # Plaintext, 'YosemiteValley'
|
||||
'9979238528357b90e2e0be549cb0b2d5999b9a4a447e5c5c7d', # Ciphertext
|
||||
'7ade65b460f5ea9be35f9e14aa883a2048e3824aa616c0b2', # Key (hash of 'BearsAhead')
|
||||
'GPG Test Vector #1',
|
||||
dict(mode='OPENPGP', iv='cd47e2afb8b7e4b0', encrypted_iv='6a7eef0b58050e8b904a' ) ),
|
||||
]
|
||||
|
||||
def get_tests(config={}):
|
||||
from Crypto.Cipher import DES3
|
||||
from common import make_block_tests
|
||||
return make_block_tests(DES3, "DES3", test_data)
|
||||
|
||||
if __name__ == '__main__':
|
||||
import unittest
|
||||
suite = lambda: unittest.TestSuite(get_tests())
|
||||
unittest.main(defaultTest='suite')
|
||||
|
||||
# vim:set ts=4 sw=4 sts=4 expandtab:
|
72
modules/Crypto/SelfTest/Cipher/test_XOR.py
Normal file
72
modules/Crypto/SelfTest/Cipher/test_XOR.py
Normal file
@ -0,0 +1,72 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# SelfTest/Cipher/XOR.py: Self-test for the XOR "cipher"
|
||||
#
|
||||
# Written in 2008 by Dwayne C. Litzenberger <dlitz@dlitz.net>
|
||||
#
|
||||
# ===================================================================
|
||||
# The contents of this file are dedicated to the public domain. To
|
||||
# the extent that dedication to the public domain is not available,
|
||||
# everyone is granted a worldwide, perpetual, royalty-free,
|
||||
# non-exclusive license to exercise all rights associated with the
|
||||
# contents of this file for any purpose whatsoever.
|
||||
# No rights are reserved.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
# SOFTWARE.
|
||||
# ===================================================================
|
||||
|
||||
"""Self-test suite for Crypto.Cipher.XOR"""
|
||||
|
||||
import unittest
|
||||
|
||||
__revision__ = "$Id$"
|
||||
|
||||
from Crypto.Util.py3compat import *
|
||||
|
||||
# This is a list of (plaintext, ciphertext, key) tuples.
|
||||
test_data = [
|
||||
# Test vectors written from scratch. (Nobody posts XOR test vectors on the web? How disappointing.)
|
||||
('01', '01',
|
||||
'00',
|
||||
'zero key'),
|
||||
|
||||
('0102040810204080', '0003050911214181',
|
||||
'01',
|
||||
'1-byte key'),
|
||||
|
||||
('0102040810204080', 'cda8c8a2dc8a8c2a',
|
||||
'ccaa',
|
||||
'2-byte key'),
|
||||
|
||||
('ff'*64, 'fffefdfcfbfaf9f8f7f6f5f4f3f2f1f0efeeedecebeae9e8e7e6e5e4e3e2e1e0'*2,
|
||||
'000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f',
|
||||
'32-byte key'),
|
||||
]
|
||||
|
||||
class TruncationSelfTest(unittest.TestCase):
|
||||
|
||||
def runTest(self):
|
||||
"""33-byte key (should raise ValueError under current implementation)"""
|
||||
# Crypto.Cipher.XOR previously truncated its inputs at 32 bytes. Now
|
||||
# it should raise a ValueError if the length is too long.
|
||||
self.assertRaises(ValueError, XOR.new, "x"*33)
|
||||
|
||||
def get_tests(config={}):
|
||||
global XOR
|
||||
from Crypto.Cipher import XOR
|
||||
from common import make_stream_tests
|
||||
return make_stream_tests(XOR, "XOR", test_data) + [TruncationSelfTest()]
|
||||
|
||||
if __name__ == '__main__':
|
||||
import unittest
|
||||
suite = lambda: unittest.TestSuite(get_tests())
|
||||
unittest.main(defaultTest='suite')
|
||||
|
||||
# vim:set ts=4 sw=4 sts=4 expandtab:
|
174
modules/Crypto/SelfTest/Cipher/test_pkcs1_15.py
Normal file
174
modules/Crypto/SelfTest/Cipher/test_pkcs1_15.py
Normal file
@ -0,0 +1,174 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# SelfTest/Cipher/test_pkcs1_15.py: Self-test for PKCS#1 v1.5 encryption
|
||||
#
|
||||
# ===================================================================
|
||||
# The contents of this file are dedicated to the public domain. To
|
||||
# the extent that dedication to the public domain is not available,
|
||||
# everyone is granted a worldwide, perpetual, royalty-free,
|
||||
# non-exclusive license to exercise all rights associated with the
|
||||
# contents of this file for any purpose whatsoever.
|
||||
# No rights are reserved.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
# SOFTWARE.
|
||||
# ===================================================================
|
||||
|
||||
__revision__ = "$Id$"
|
||||
|
||||
import unittest
|
||||
import sys
|
||||
|
||||
from Crypto.PublicKey import RSA
|
||||
from Crypto.SelfTest.st_common import list_test_cases, a2b_hex, b2a_hex
|
||||
from Crypto import Random
|
||||
from Crypto.Cipher import PKCS1_v1_5 as PKCS
|
||||
from Crypto.Util.py3compat import *
|
||||
|
||||
def rws(t):
|
||||
"""Remove white spaces, tabs, and new lines from a string"""
|
||||
for c in ['\n', '\t', ' ']:
|
||||
t = t.replace(c,'')
|
||||
return t
|
||||
|
||||
def t2b(t):
|
||||
"""Convert a text string with bytes in hex form to a byte string"""
|
||||
clean = b(rws(t))
|
||||
if len(clean)%2 == 1:
|
||||
print clean
|
||||
raise ValueError("Even number of characters expected")
|
||||
return a2b_hex(clean)
|
||||
|
||||
class PKCS1_15_Tests(unittest.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
self.rng = Random.new().read
|
||||
self.key1024 = RSA.generate(1024, self.rng)
|
||||
|
||||
# List of tuples with test data for PKCS#1 v1.5.
|
||||
# Each tuple is made up by:
|
||||
# Item #0: dictionary with RSA key component, or key to import
|
||||
# Item #1: plaintext
|
||||
# Item #2: ciphertext
|
||||
# Item #3: random data
|
||||
|
||||
_testData = (
|
||||
|
||||
#
|
||||
# Generated with openssl 0.9.8o
|
||||
#
|
||||
(
|
||||
# Private key
|
||||
'''-----BEGIN RSA PRIVATE KEY-----
|
||||
MIICXAIBAAKBgQDAiAnvIAOvqVwJTaYzsKnefZftgtXGE2hPJppGsWl78yz9jeXY
|
||||
W/FxX/gTPURArNhdnhP6n3p2ZaDIBrO2zizbgIXs0IsljTTcr4vnI8fMXzyNUOjA
|
||||
zP3nzMqZDZK6757XQAobOssMkBFqRWwilT/3DsBhRpl3iMUhF+wvpTSHewIDAQAB
|
||||
AoGAC4HV/inOrpgTvSab8Wj0riyZgQOZ3U3ZpSlsfR8ra9Ib9Uee3jCYnKscu6Gk
|
||||
y6zI/cdt8EPJ4PuwAWSNJzbpbVaDvUq25OD+CX8/uRT08yBS4J8TzBitZJTD4lS7
|
||||
atdTnKT0Wmwk+u8tDbhvMKwnUHdJLcuIsycts9rwJVapUtkCQQDvDpx2JMun0YKG
|
||||
uUttjmL8oJ3U0m3ZvMdVwBecA0eebZb1l2J5PvI3EJD97eKe91Nsw8T3lwpoN40k
|
||||
IocSVDklAkEAzi1HLHE6EzVPOe5+Y0kGvrIYRRhncOb72vCvBZvD6wLZpQgqo6c4
|
||||
d3XHFBBQWA6xcvQb5w+VVEJZzw64y25sHwJBAMYReRl6SzL0qA0wIYrYWrOt8JeQ
|
||||
8mthulcWHXmqTgC6FEXP9Es5GD7/fuKl4wqLKZgIbH4nqvvGay7xXLCXD/ECQH9a
|
||||
1JYNMtRen5unSAbIOxRcKkWz92F0LKpm9ZW/S9vFHO+mBcClMGoKJHiuQxLBsLbT
|
||||
NtEZfSJZAeS2sUtn3/0CQDb2M2zNBTF8LlM0nxmh0k9VGm5TVIyBEMcipmvOgqIs
|
||||
HKukWBcq9f/UOmS0oEhai/6g+Uf7VHJdWaeO5LzuvwU=
|
||||
-----END RSA PRIVATE KEY-----''',
|
||||
# Plaintext
|
||||
'''THIS IS PLAINTEXT\x0A''',
|
||||
# Ciphertext
|
||||
'''3f dc fd 3c cd 5c 9b 12 af 65 32 e3 f7 d0 da 36
|
||||
8f 8f d9 e3 13 1c 7f c8 b3 f9 c1 08 e4 eb 79 9c
|
||||
91 89 1f 96 3b 94 77 61 99 a4 b1 ee 5d e6 17 c9
|
||||
5d 0a b5 63 52 0a eb 00 45 38 2a fb b0 71 3d 11
|
||||
f7 a1 9e a7 69 b3 af 61 c0 bb 04 5b 5d 4b 27 44
|
||||
1f 5b 97 89 ba 6a 08 95 ee 4f a2 eb 56 64 e5 0f
|
||||
da 7c f9 9a 61 61 06 62 ed a0 bc 5f aa 6c 31 78
|
||||
70 28 1a bb 98 3c e3 6a 60 3c d1 0b 0f 5a f4 75''',
|
||||
# Random data
|
||||
'''eb d7 7d 86 a4 35 23 a3 54 7e 02 0b 42 1d
|
||||
61 6c af 67 b8 4e 17 56 80 66 36 04 64 34 26 8a
|
||||
47 dd 44 b3 1a b2 17 60 f4 91 2e e2 b5 95 64 cc
|
||||
f9 da c8 70 94 54 86 4c ef 5b 08 7d 18 c4 ab 8d
|
||||
04 06 33 8f ca 15 5f 52 60 8a a1 0c f5 08 b5 4c
|
||||
bb 99 b8 94 25 04 9c e6 01 75 e6 f9 63 7a 65 61
|
||||
13 8a a7 47 77 81 ae 0d b8 2c 4d 50 a5'''
|
||||
),
|
||||
)
|
||||
|
||||
def testEncrypt1(self):
|
||||
for test in self._testData:
|
||||
# Build the key
|
||||
key = RSA.importKey(test[0])
|
||||
# RNG that takes its random numbers from a pool given
|
||||
# at initialization
|
||||
class randGen:
|
||||
def __init__(self, data):
|
||||
self.data = data
|
||||
self.idx = 0
|
||||
def __call__(self, N):
|
||||
r = self.data[self.idx:N]
|
||||
self.idx += N
|
||||
return r
|
||||
# The real test
|
||||
key._randfunc = randGen(t2b(test[3]))
|
||||
cipher = PKCS.new(key)
|
||||
ct = cipher.encrypt(b(test[1]))
|
||||
self.assertEqual(ct, t2b(test[2]))
|
||||
|
||||
def testEncrypt2(self):
|
||||
# Verify that encryption fail if plaintext is too long
|
||||
pt = '\x00'*(128-11+1)
|
||||
cipher = PKCS.new(self.key1024)
|
||||
self.assertRaises(ValueError, cipher.encrypt, pt)
|
||||
|
||||
def testVerify1(self):
|
||||
for test in self._testData:
|
||||
# Build the key
|
||||
key = RSA.importKey(test[0])
|
||||
# The real test
|
||||
cipher = PKCS.new(key)
|
||||
pt = cipher.decrypt(t2b(test[2]), "---")
|
||||
self.assertEqual(pt, b(test[1]))
|
||||
|
||||
def testVerify2(self):
|
||||
# Verify that decryption fails if ciphertext is not as long as
|
||||
# RSA modulus
|
||||
cipher = PKCS.new(self.key1024)
|
||||
self.assertRaises(ValueError, cipher.decrypt, '\x00'*127, "---")
|
||||
self.assertRaises(ValueError, cipher.decrypt, '\x00'*129, "---")
|
||||
|
||||
# Verify that decryption fails if there are less then 8 non-zero padding
|
||||
# bytes
|
||||
pt = b('\x00\x02' + '\xFF'*7 + '\x00' + '\x45'*118)
|
||||
ct = self.key1024.encrypt(pt, 0)[0]
|
||||
ct = b('\x00'*(128-len(ct))) + ct
|
||||
self.assertEqual("---", cipher.decrypt(ct, "---"))
|
||||
|
||||
def testEncryptVerify1(self):
|
||||
# Encrypt/Verify messages of length [0..RSAlen-11]
|
||||
# and therefore padding [8..117]
|
||||
for pt_len in xrange(0,128-11+1):
|
||||
pt = self.rng(pt_len)
|
||||
cipher = PKCS.new(self.key1024)
|
||||
ct = cipher.encrypt(pt)
|
||||
pt2 = cipher.decrypt(ct, "---")
|
||||
self.assertEqual(pt,pt2)
|
||||
|
||||
|
||||
def get_tests(config={}):
|
||||
tests = []
|
||||
tests += list_test_cases(PKCS1_15_Tests)
|
||||
return tests
|
||||
|
||||
if __name__ == '__main__':
|
||||
suite = lambda: unittest.TestSuite(get_tests())
|
||||
unittest.main(defaultTest='suite')
|
||||
|
||||
# vim:set ts=4 sw=4 sts=4 expandtab:
|
373
modules/Crypto/SelfTest/Cipher/test_pkcs1_oaep.py
Normal file
373
modules/Crypto/SelfTest/Cipher/test_pkcs1_oaep.py
Normal file
@ -0,0 +1,373 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# SelfTest/Cipher/test_pkcs1_oaep.py: Self-test for PKCS#1 OAEP encryption
|
||||
#
|
||||
# ===================================================================
|
||||
# The contents of this file are dedicated to the public domain. To
|
||||
# the extent that dedication to the public domain is not available,
|
||||
# everyone is granted a worldwide, perpetual, royalty-free,
|
||||
# non-exclusive license to exercise all rights associated with the
|
||||
# contents of this file for any purpose whatsoever.
|
||||
# No rights are reserved.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
# SOFTWARE.
|
||||
# ===================================================================
|
||||
|
||||
from __future__ import nested_scopes
|
||||
|
||||
__revision__ = "$Id$"
|
||||
|
||||
import unittest
|
||||
|
||||
from Crypto.SelfTest.st_common import list_test_cases, a2b_hex, b2a_hex
|
||||
|
||||
from Crypto.Util.py3compat import *
|
||||
from Crypto.PublicKey import RSA
|
||||
from Crypto.Cipher import PKCS1_OAEP as PKCS
|
||||
from Crypto.Hash import MD2,MD5,SHA1,SHA256,RIPEMD160
|
||||
from Crypto import Random
|
||||
|
||||
def rws(t):
|
||||
"""Remove white spaces, tabs, and new lines from a string"""
|
||||
for c in ['\n', '\t', ' ']:
|
||||
t = t.replace(c,'')
|
||||
return t
|
||||
|
||||
def t2b(t):
|
||||
"""Convert a text string with bytes in hex form to a byte string"""
|
||||
clean = rws(t)
|
||||
if len(clean)%2 == 1:
|
||||
raise ValueError("Even number of characters expected")
|
||||
return a2b_hex(clean)
|
||||
|
||||
class PKCS1_OAEP_Tests(unittest.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
self.rng = Random.new().read
|
||||
self.key1024 = RSA.generate(1024, self.rng)
|
||||
|
||||
# List of tuples with test data for PKCS#1 OAEP
|
||||
# Each tuple is made up by:
|
||||
# Item #0: dictionary with RSA key component
|
||||
# Item #1: plaintext
|
||||
# Item #2: ciphertext
|
||||
# Item #3: random data (=seed)
|
||||
# Item #4: hash object
|
||||
|
||||
_testData = (
|
||||
|
||||
#
|
||||
# From in oaep-int.txt to be found in
|
||||
# ftp://ftp.rsasecurity.com/pub/pkcs/pkcs-1/pkcs-1v2-1-vec.zip
|
||||
#
|
||||
(
|
||||
# Private key
|
||||
{
|
||||
'n':'''bb f8 2f 09 06 82 ce 9c 23 38 ac 2b 9d a8 71 f7
|
||||
36 8d 07 ee d4 10 43 a4 40 d6 b6 f0 74 54 f5 1f
|
||||
b8 df ba af 03 5c 02 ab 61 ea 48 ce eb 6f cd 48
|
||||
76 ed 52 0d 60 e1 ec 46 19 71 9d 8a 5b 8b 80 7f
|
||||
af b8 e0 a3 df c7 37 72 3e e6 b4 b7 d9 3a 25 84
|
||||
ee 6a 64 9d 06 09 53 74 88 34 b2 45 45 98 39 4e
|
||||
e0 aa b1 2d 7b 61 a5 1f 52 7a 9a 41 f6 c1 68 7f
|
||||
e2 53 72 98 ca 2a 8f 59 46 f8 e5 fd 09 1d bd cb''',
|
||||
# Public key
|
||||
'e':'11',
|
||||
# In the test vector, only p and q were given...
|
||||
# d is computed offline as e^{-1} mod (p-1)(q-1)
|
||||
'd':'''a5dafc5341faf289c4b988db30c1cdf83f31251e0
|
||||
668b42784813801579641b29410b3c7998d6bc465745e5c3
|
||||
92669d6870da2c082a939e37fdcb82ec93edac97ff3ad595
|
||||
0accfbc111c76f1a9529444e56aaf68c56c092cd38dc3bef
|
||||
5d20a939926ed4f74a13eddfbe1a1cecc4894af9428c2b7b
|
||||
8883fe4463a4bc85b1cb3c1'''
|
||||
}
|
||||
,
|
||||
# Plaintext
|
||||
'''d4 36 e9 95 69 fd 32 a7 c8 a0 5b bc 90 d3 2c 49''',
|
||||
# Ciphertext
|
||||
'''12 53 e0 4d c0 a5 39 7b b4 4a 7a b8 7e 9b f2 a0
|
||||
39 a3 3d 1e 99 6f c8 2a 94 cc d3 00 74 c9 5d f7
|
||||
63 72 20 17 06 9e 52 68 da 5d 1c 0b 4f 87 2c f6
|
||||
53 c1 1d f8 23 14 a6 79 68 df ea e2 8d ef 04 bb
|
||||
6d 84 b1 c3 1d 65 4a 19 70 e5 78 3b d6 eb 96 a0
|
||||
24 c2 ca 2f 4a 90 fe 9f 2e f5 c9 c1 40 e5 bb 48
|
||||
da 95 36 ad 87 00 c8 4f c9 13 0a de a7 4e 55 8d
|
||||
51 a7 4d df 85 d8 b5 0d e9 68 38 d6 06 3e 09 55''',
|
||||
# Random
|
||||
'''aa fd 12 f6 59 ca e6 34 89 b4 79 e5 07 6d de c2
|
||||
f0 6c b5 8f''',
|
||||
# Hash
|
||||
SHA1,
|
||||
),
|
||||
|
||||
#
|
||||
# From in oaep-vect.txt to be found in Example 1.1
|
||||
# ftp://ftp.rsasecurity.com/pub/pkcs/pkcs-1/pkcs-1v2-1-vec.zip
|
||||
#
|
||||
(
|
||||
# Private key
|
||||
{
|
||||
'n':'''a8 b3 b2 84 af 8e b5 0b 38 70 34 a8 60 f1 46 c4
|
||||
91 9f 31 87 63 cd 6c 55 98 c8 ae 48 11 a1 e0 ab
|
||||
c4 c7 e0 b0 82 d6 93 a5 e7 fc ed 67 5c f4 66 85
|
||||
12 77 2c 0c bc 64 a7 42 c6 c6 30 f5 33 c8 cc 72
|
||||
f6 2a e8 33 c4 0b f2 58 42 e9 84 bb 78 bd bf 97
|
||||
c0 10 7d 55 bd b6 62 f5 c4 e0 fa b9 84 5c b5 14
|
||||
8e f7 39 2d d3 aa ff 93 ae 1e 6b 66 7b b3 d4 24
|
||||
76 16 d4 f5 ba 10 d4 cf d2 26 de 88 d3 9f 16 fb''',
|
||||
'e':'''01 00 01''',
|
||||
'd':'''53 33 9c fd b7 9f c8 46 6a 65 5c 73 16 ac a8 5c
|
||||
55 fd 8f 6d d8 98 fd af 11 95 17 ef 4f 52 e8 fd
|
||||
8e 25 8d f9 3f ee 18 0f a0 e4 ab 29 69 3c d8 3b
|
||||
15 2a 55 3d 4a c4 d1 81 2b 8b 9f a5 af 0e 7f 55
|
||||
fe 73 04 df 41 57 09 26 f3 31 1f 15 c4 d6 5a 73
|
||||
2c 48 31 16 ee 3d 3d 2d 0a f3 54 9a d9 bf 7c bf
|
||||
b7 8a d8 84 f8 4d 5b eb 04 72 4d c7 36 9b 31 de
|
||||
f3 7d 0c f5 39 e9 cf cd d3 de 65 37 29 ea d5 d1 '''
|
||||
}
|
||||
,
|
||||
# Plaintext
|
||||
'''66 28 19 4e 12 07 3d b0 3b a9 4c da 9e f9 53 23
|
||||
97 d5 0d ba 79 b9 87 00 4a fe fe 34''',
|
||||
# Ciphertext
|
||||
'''35 4f e6 7b 4a 12 6d 5d 35 fe 36 c7 77 79 1a 3f
|
||||
7b a1 3d ef 48 4e 2d 39 08 af f7 22 fa d4 68 fb
|
||||
21 69 6d e9 5d 0b e9 11 c2 d3 17 4f 8a fc c2 01
|
||||
03 5f 7b 6d 8e 69 40 2d e5 45 16 18 c2 1a 53 5f
|
||||
a9 d7 bf c5 b8 dd 9f c2 43 f8 cf 92 7d b3 13 22
|
||||
d6 e8 81 ea a9 1a 99 61 70 e6 57 a0 5a 26 64 26
|
||||
d9 8c 88 00 3f 84 77 c1 22 70 94 a0 d9 fa 1e 8c
|
||||
40 24 30 9c e1 ec cc b5 21 00 35 d4 7a c7 2e 8a''',
|
||||
# Random
|
||||
'''18 b7 76 ea 21 06 9d 69 77 6a 33 e9 6b ad 48 e1
|
||||
dd a0 a5 ef''',
|
||||
SHA1
|
||||
),
|
||||
|
||||
#
|
||||
# From in oaep-vect.txt to be found in Example 2.1
|
||||
# ftp://ftp.rsasecurity.com/pub/pkcs/pkcs-1/pkcs-1v2-1-vec.zip
|
||||
#
|
||||
(
|
||||
# Private key
|
||||
{
|
||||
'n':'''01 94 7c 7f ce 90 42 5f 47 27 9e 70 85 1f 25 d5
|
||||
e6 23 16 fe 8a 1d f1 93 71 e3 e6 28 e2 60 54 3e
|
||||
49 01 ef 60 81 f6 8c 0b 81 41 19 0d 2a e8 da ba
|
||||
7d 12 50 ec 6d b6 36 e9 44 ec 37 22 87 7c 7c 1d
|
||||
0a 67 f1 4b 16 94 c5 f0 37 94 51 a4 3e 49 a3 2d
|
||||
de 83 67 0b 73 da 91 a1 c9 9b c2 3b 43 6a 60 05
|
||||
5c 61 0f 0b af 99 c1 a0 79 56 5b 95 a3 f1 52 66
|
||||
32 d1 d4 da 60 f2 0e da 25 e6 53 c4 f0 02 76 6f
|
||||
45''',
|
||||
'e':'''01 00 01''',
|
||||
'd':'''08 23 f2 0f ad b5 da 89 08 8a 9d 00 89 3e 21 fa
|
||||
4a 1b 11 fb c9 3c 64 a3 be 0b aa ea 97 fb 3b 93
|
||||
c3 ff 71 37 04 c1 9c 96 3c 1d 10 7a ae 99 05 47
|
||||
39 f7 9e 02 e1 86 de 86 f8 7a 6d de fe a6 d8 cc
|
||||
d1 d3 c8 1a 47 bf a7 25 5b e2 06 01 a4 a4 b2 f0
|
||||
8a 16 7b 5e 27 9d 71 5b 1b 45 5b dd 7e ab 24 59
|
||||
41 d9 76 8b 9a ce fb 3c cd a5 95 2d a3 ce e7 25
|
||||
25 b4 50 16 63 a8 ee 15 c9 e9 92 d9 24 62 fe 39'''
|
||||
},
|
||||
# Plaintext
|
||||
'''8f f0 0c aa 60 5c 70 28 30 63 4d 9a 6c 3d 42 c6
|
||||
52 b5 8c f1 d9 2f ec 57 0b ee e7''',
|
||||
# Ciphertext
|
||||
'''01 81 af 89 22 b9 fc b4 d7 9d 92 eb e1 98 15 99
|
||||
2f c0 c1 43 9d 8b cd 49 13 98 a0 f4 ad 3a 32 9a
|
||||
5b d9 38 55 60 db 53 26 83 c8 b7 da 04 e4 b1 2a
|
||||
ed 6a ac df 47 1c 34 c9 cd a8 91 ad dc c2 df 34
|
||||
56 65 3a a6 38 2e 9a e5 9b 54 45 52 57 eb 09 9d
|
||||
56 2b be 10 45 3f 2b 6d 13 c5 9c 02 e1 0f 1f 8a
|
||||
bb 5d a0 d0 57 09 32 da cf 2d 09 01 db 72 9d 0f
|
||||
ef cc 05 4e 70 96 8e a5 40 c8 1b 04 bc ae fe 72
|
||||
0e''',
|
||||
# Random
|
||||
'''8c 40 7b 5e c2 89 9e 50 99 c5 3e 8c e7 93 bf 94
|
||||
e7 1b 17 82''',
|
||||
SHA1
|
||||
),
|
||||
|
||||
#
|
||||
# From in oaep-vect.txt to be found in Example 10.1
|
||||
# ftp://ftp.rsasecurity.com/pub/pkcs/pkcs-1/pkcs-1v2-1-vec.zip
|
||||
#
|
||||
(
|
||||
# Private key
|
||||
{
|
||||
'n':'''ae 45 ed 56 01 ce c6 b8 cc 05 f8 03 93 5c 67 4d
|
||||
db e0 d7 5c 4c 09 fd 79 51 fc 6b 0c ae c3 13 a8
|
||||
df 39 97 0c 51 8b ff ba 5e d6 8f 3f 0d 7f 22 a4
|
||||
02 9d 41 3f 1a e0 7e 4e be 9e 41 77 ce 23 e7 f5
|
||||
40 4b 56 9e 4e e1 bd cf 3c 1f b0 3e f1 13 80 2d
|
||||
4f 85 5e b9 b5 13 4b 5a 7c 80 85 ad ca e6 fa 2f
|
||||
a1 41 7e c3 76 3b e1 71 b0 c6 2b 76 0e de 23 c1
|
||||
2a d9 2b 98 08 84 c6 41 f5 a8 fa c2 6b da d4 a0
|
||||
33 81 a2 2f e1 b7 54 88 50 94 c8 25 06 d4 01 9a
|
||||
53 5a 28 6a fe b2 71 bb 9b a5 92 de 18 dc f6 00
|
||||
c2 ae ea e5 6e 02 f7 cf 79 fc 14 cf 3b dc 7c d8
|
||||
4f eb bb f9 50 ca 90 30 4b 22 19 a7 aa 06 3a ef
|
||||
a2 c3 c1 98 0e 56 0c d6 4a fe 77 95 85 b6 10 76
|
||||
57 b9 57 85 7e fd e6 01 09 88 ab 7d e4 17 fc 88
|
||||
d8 f3 84 c4 e6 e7 2c 3f 94 3e 0c 31 c0 c4 a5 cc
|
||||
36 f8 79 d8 a3 ac 9d 7d 59 86 0e aa da 6b 83 bb''',
|
||||
'e':'''01 00 01''',
|
||||
'd':'''05 6b 04 21 6f e5 f3 54 ac 77 25 0a 4b 6b 0c 85
|
||||
25 a8 5c 59 b0 bd 80 c5 64 50 a2 2d 5f 43 8e 59
|
||||
6a 33 3a a8 75 e2 91 dd 43 f4 8c b8 8b 9d 5f c0
|
||||
d4 99 f9 fc d1 c3 97 f9 af c0 70 cd 9e 39 8c 8d
|
||||
19 e6 1d b7 c7 41 0a 6b 26 75 df bf 5d 34 5b 80
|
||||
4d 20 1a dd 50 2d 5c e2 df cb 09 1c e9 99 7b be
|
||||
be 57 30 6f 38 3e 4d 58 81 03 f0 36 f7 e8 5d 19
|
||||
34 d1 52 a3 23 e4 a8 db 45 1d 6f 4a 5b 1b 0f 10
|
||||
2c c1 50 e0 2f ee e2 b8 8d ea 4a d4 c1 ba cc b2
|
||||
4d 84 07 2d 14 e1 d2 4a 67 71 f7 40 8e e3 05 64
|
||||
fb 86 d4 39 3a 34 bc f0 b7 88 50 1d 19 33 03 f1
|
||||
3a 22 84 b0 01 f0 f6 49 ea f7 93 28 d4 ac 5c 43
|
||||
0a b4 41 49 20 a9 46 0e d1 b7 bc 40 ec 65 3e 87
|
||||
6d 09 ab c5 09 ae 45 b5 25 19 01 16 a0 c2 61 01
|
||||
84 82 98 50 9c 1c 3b f3 a4 83 e7 27 40 54 e1 5e
|
||||
97 07 50 36 e9 89 f6 09 32 80 7b 52 57 75 1e 79'''
|
||||
},
|
||||
# Plaintext
|
||||
'''8b ba 6b f8 2a 6c 0f 86 d5 f1 75 6e 97 95 68 70
|
||||
b0 89 53 b0 6b 4e b2 05 bc 16 94 ee''',
|
||||
# Ciphertext
|
||||
'''53 ea 5d c0 8c d2 60 fb 3b 85 85 67 28 7f a9 15
|
||||
52 c3 0b 2f eb fb a2 13 f0 ae 87 70 2d 06 8d 19
|
||||
ba b0 7f e5 74 52 3d fb 42 13 9d 68 c3 c5 af ee
|
||||
e0 bf e4 cb 79 69 cb f3 82 b8 04 d6 e6 13 96 14
|
||||
4e 2d 0e 60 74 1f 89 93 c3 01 4b 58 b9 b1 95 7a
|
||||
8b ab cd 23 af 85 4f 4c 35 6f b1 66 2a a7 2b fc
|
||||
c7 e5 86 55 9d c4 28 0d 16 0c 12 67 85 a7 23 eb
|
||||
ee be ff 71 f1 15 94 44 0a ae f8 7d 10 79 3a 87
|
||||
74 a2 39 d4 a0 4c 87 fe 14 67 b9 da f8 52 08 ec
|
||||
6c 72 55 79 4a 96 cc 29 14 2f 9a 8b d4 18 e3 c1
|
||||
fd 67 34 4b 0c d0 82 9d f3 b2 be c6 02 53 19 62
|
||||
93 c6 b3 4d 3f 75 d3 2f 21 3d d4 5c 62 73 d5 05
|
||||
ad f4 cc ed 10 57 cb 75 8f c2 6a ee fa 44 12 55
|
||||
ed 4e 64 c1 99 ee 07 5e 7f 16 64 61 82 fd b4 64
|
||||
73 9b 68 ab 5d af f0 e6 3e 95 52 01 68 24 f0 54
|
||||
bf 4d 3c 8c 90 a9 7b b6 b6 55 32 84 eb 42 9f cc''',
|
||||
# Random
|
||||
'''47 e1 ab 71 19 fe e5 6c 95 ee 5e aa d8 6f 40 d0
|
||||
aa 63 bd 33''',
|
||||
SHA1
|
||||
),
|
||||
)
|
||||
|
||||
def testEncrypt1(self):
|
||||
# Verify encryption using all test vectors
|
||||
for test in self._testData:
|
||||
# Build the key
|
||||
comps = [ long(rws(test[0][x]),16) for x in ('n','e') ]
|
||||
key = RSA.construct(comps)
|
||||
# RNG that takes its random numbers from a pool given
|
||||
# at initialization
|
||||
class randGen:
|
||||
def __init__(self, data):
|
||||
self.data = data
|
||||
self.idx = 0
|
||||
def __call__(self, N):
|
||||
r = self.data[self.idx:N]
|
||||
self.idx += N
|
||||
return r
|
||||
# The real test
|
||||
key._randfunc = randGen(t2b(test[3]))
|
||||
cipher = PKCS.new(key, test[4])
|
||||
ct = cipher.encrypt(t2b(test[1]))
|
||||
self.assertEqual(ct, t2b(test[2]))
|
||||
|
||||
def testEncrypt2(self):
|
||||
# Verify that encryption fails if plaintext is too long
|
||||
pt = '\x00'*(128-2*20-2+1)
|
||||
cipher = PKCS.new(self.key1024)
|
||||
self.assertRaises(ValueError, cipher.encrypt, pt)
|
||||
|
||||
def testDecrypt1(self):
|
||||
# Verify decryption using all test vectors
|
||||
for test in self._testData:
|
||||
# Build the key
|
||||
comps = [ long(rws(test[0][x]),16) for x in ('n','e','d') ]
|
||||
key = RSA.construct(comps)
|
||||
# The real test
|
||||
cipher = PKCS.new(key, test[4])
|
||||
pt = cipher.decrypt(t2b(test[2]))
|
||||
self.assertEqual(pt, t2b(test[1]))
|
||||
|
||||
def testDecrypt2(self):
|
||||
# Simplest possible negative tests
|
||||
for ct_size in (127,128,129):
|
||||
cipher = PKCS.new(self.key1024)
|
||||
self.assertRaises(ValueError, cipher.decrypt, bchr(0x00)*ct_size)
|
||||
|
||||
def testEncryptDecrypt1(self):
|
||||
# Encrypt/Decrypt messages of length [0..128-2*20-2]
|
||||
for pt_len in xrange(0,128-2*20-2):
|
||||
pt = self.rng(pt_len)
|
||||
cipher = PKCS.new(self.key1024)
|
||||
ct = cipher.encrypt(pt)
|
||||
pt2 = cipher.decrypt(ct)
|
||||
self.assertEqual(pt,pt2)
|
||||
|
||||
def testEncryptDecrypt2(self):
|
||||
# Helper function to monitor what's requested from RNG
|
||||
global asked
|
||||
def localRng(N):
|
||||
global asked
|
||||
asked += N
|
||||
return self.rng(N)
|
||||
# Verify that OAEP is friendly to all hashes
|
||||
for hashmod in (MD2,MD5,SHA1,SHA256,RIPEMD160):
|
||||
# Verify that encrypt() asks for as many random bytes
|
||||
# as the hash output size
|
||||
asked = 0
|
||||
pt = self.rng(40)
|
||||
self.key1024._randfunc = localRng
|
||||
cipher = PKCS.new(self.key1024, hashmod)
|
||||
ct = cipher.encrypt(pt)
|
||||
self.assertEqual(cipher.decrypt(ct), pt)
|
||||
self.failUnless(asked > hashmod.digest_size)
|
||||
|
||||
def testEncryptDecrypt3(self):
|
||||
# Verify that OAEP supports labels
|
||||
pt = self.rng(35)
|
||||
xlabel = self.rng(22)
|
||||
cipher = PKCS.new(self.key1024, label=xlabel)
|
||||
ct = cipher.encrypt(pt)
|
||||
self.assertEqual(cipher.decrypt(ct), pt)
|
||||
|
||||
def testEncryptDecrypt4(self):
|
||||
# Verify that encrypt() uses the custom MGF
|
||||
global mgfcalls
|
||||
# Helper function to monitor what's requested from MGF
|
||||
def newMGF(seed,maskLen):
|
||||
global mgfcalls
|
||||
mgfcalls += 1
|
||||
return bchr(0x00)*maskLen
|
||||
mgfcalls = 0
|
||||
pt = self.rng(32)
|
||||
cipher = PKCS.new(self.key1024, mgfunc=newMGF)
|
||||
ct = cipher.encrypt(pt)
|
||||
self.assertEqual(mgfcalls, 2)
|
||||
self.assertEqual(cipher.decrypt(ct), pt)
|
||||
|
||||
def get_tests(config={}):
|
||||
tests = []
|
||||
tests += list_test_cases(PKCS1_OAEP_Tests)
|
||||
return tests
|
||||
|
||||
if __name__ == '__main__':
|
||||
suite = lambda: unittest.TestSuite(get_tests())
|
||||
unittest.main(defaultTest='suite')
|
||||
|
||||
# vim:set ts=4 sw=4 sts=4 expandtab:
|
53
modules/Crypto/SelfTest/Hash/__init__.py
Normal file
53
modules/Crypto/SelfTest/Hash/__init__.py
Normal file
@ -0,0 +1,53 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# SelfTest/Hash/__init__.py: Self-test for hash modules
|
||||
#
|
||||
# Written in 2008 by Dwayne C. Litzenberger <dlitz@dlitz.net>
|
||||
#
|
||||
# ===================================================================
|
||||
# The contents of this file are dedicated to the public domain. To
|
||||
# the extent that dedication to the public domain is not available,
|
||||
# everyone is granted a worldwide, perpetual, royalty-free,
|
||||
# non-exclusive license to exercise all rights associated with the
|
||||
# contents of this file for any purpose whatsoever.
|
||||
# No rights are reserved.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
# SOFTWARE.
|
||||
# ===================================================================
|
||||
|
||||
"""Self-test for hash modules"""
|
||||
|
||||
__revision__ = "$Id$"
|
||||
|
||||
def get_tests(config={}):
|
||||
tests = []
|
||||
from Crypto.SelfTest.Hash import test_HMAC; tests += test_HMAC.get_tests(config=config)
|
||||
from Crypto.SelfTest.Hash import test_CMAC; tests += test_CMAC.get_tests(config=config)
|
||||
from Crypto.SelfTest.Hash import test_MD2; tests += test_MD2.get_tests(config=config)
|
||||
from Crypto.SelfTest.Hash import test_MD4; tests += test_MD4.get_tests(config=config)
|
||||
from Crypto.SelfTest.Hash import test_MD5; tests += test_MD5.get_tests(config=config)
|
||||
from Crypto.SelfTest.Hash import test_RIPEMD160; tests += test_RIPEMD160.get_tests(config=config)
|
||||
from Crypto.SelfTest.Hash import test_SHA1; tests += test_SHA1.get_tests(config=config)
|
||||
from Crypto.SelfTest.Hash import test_SHA256; tests += test_SHA256.get_tests(config=config)
|
||||
try:
|
||||
from Crypto.SelfTest.Hash import test_SHA224; tests += test_SHA224.get_tests(config=config)
|
||||
from Crypto.SelfTest.Hash import test_SHA384; tests += test_SHA384.get_tests(config=config)
|
||||
from Crypto.SelfTest.Hash import test_SHA512; tests += test_SHA512.get_tests(config=config)
|
||||
except ImportError:
|
||||
import sys
|
||||
sys.stderr.write("SelfTest: warning: not testing SHA224/SHA384/SHA512 modules (not available)\n")
|
||||
return tests
|
||||
|
||||
if __name__ == '__main__':
|
||||
import unittest
|
||||
suite = lambda: unittest.TestSuite(get_tests())
|
||||
unittest.main(defaultTest='suite')
|
||||
|
||||
# vim:set ts=4 sw=4 sts=4 expandtab:
|
259
modules/Crypto/SelfTest/Hash/common.py
Normal file
259
modules/Crypto/SelfTest/Hash/common.py
Normal file
@ -0,0 +1,259 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# SelfTest/Hash/common.py: Common code for Crypto.SelfTest.Hash
|
||||
#
|
||||
# Written in 2008 by Dwayne C. Litzenberger <dlitz@dlitz.net>
|
||||
#
|
||||
# ===================================================================
|
||||
# The contents of this file are dedicated to the public domain. To
|
||||
# the extent that dedication to the public domain is not available,
|
||||
# everyone is granted a worldwide, perpetual, royalty-free,
|
||||
# non-exclusive license to exercise all rights associated with the
|
||||
# contents of this file for any purpose whatsoever.
|
||||
# No rights are reserved.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
# SOFTWARE.
|
||||
# ===================================================================
|
||||
|
||||
"""Self-testing for PyCrypto hash modules"""
|
||||
|
||||
__revision__ = "$Id$"
|
||||
|
||||
import sys
|
||||
import unittest
|
||||
import binascii
|
||||
import Crypto.Hash
|
||||
from Crypto.Util.py3compat import *
|
||||
if sys.version_info[0] == 2 and sys.version_info[1] == 1:
|
||||
from Crypto.Util.py21compat import *
|
||||
|
||||
# For compatibility with Python 2.1 and Python 2.2
|
||||
if sys.hexversion < 0x02030000:
|
||||
# Python 2.1 doesn't have a dict() function
|
||||
# Python 2.2 dict() function raises TypeError if you do dict(MD5='blah')
|
||||
def dict(**kwargs):
|
||||
return kwargs.copy()
|
||||
else:
|
||||
dict = dict
|
||||
|
||||
from Crypto.Util.strxor import strxor_c
|
||||
|
||||
class HashDigestSizeSelfTest(unittest.TestCase):
|
||||
|
||||
def __init__(self, hashmod, description, expected):
|
||||
unittest.TestCase.__init__(self)
|
||||
self.hashmod = hashmod
|
||||
self.expected = expected
|
||||
self.description = description
|
||||
|
||||
def shortDescription(self):
|
||||
return self.description
|
||||
|
||||
def runTest(self):
|
||||
self.failUnless(hasattr(self.hashmod, "digest_size"))
|
||||
self.assertEquals(self.hashmod.digest_size, self.expected)
|
||||
h = self.hashmod.new()
|
||||
self.failUnless(hasattr(h, "digest_size"))
|
||||
self.assertEquals(h.digest_size, self.expected)
|
||||
|
||||
|
||||
class HashSelfTest(unittest.TestCase):
|
||||
|
||||
def __init__(self, hashmod, description, expected, input):
|
||||
unittest.TestCase.__init__(self)
|
||||
self.hashmod = hashmod
|
||||
self.expected = expected
|
||||
self.input = input
|
||||
self.description = description
|
||||
|
||||
def shortDescription(self):
|
||||
return self.description
|
||||
|
||||
def runTest(self):
|
||||
h = self.hashmod.new()
|
||||
h.update(self.input)
|
||||
|
||||
out1 = binascii.b2a_hex(h.digest())
|
||||
out2 = h.hexdigest()
|
||||
|
||||
h = self.hashmod.new(self.input)
|
||||
|
||||
out3 = h.hexdigest()
|
||||
out4 = binascii.b2a_hex(h.digest())
|
||||
|
||||
# PY3K: hexdigest() should return str(), and digest() bytes
|
||||
self.assertEqual(self.expected, out1) # h = .new(); h.update(data); h.digest()
|
||||
if sys.version_info[0] == 2:
|
||||
self.assertEqual(self.expected, out2) # h = .new(); h.update(data); h.hexdigest()
|
||||
self.assertEqual(self.expected, out3) # h = .new(data); h.hexdigest()
|
||||
else:
|
||||
self.assertEqual(self.expected.decode(), out2) # h = .new(); h.update(data); h.hexdigest()
|
||||
self.assertEqual(self.expected.decode(), out3) # h = .new(data); h.hexdigest()
|
||||
self.assertEqual(self.expected, out4) # h = .new(data); h.digest()
|
||||
|
||||
# Verify that the .new() method produces a fresh hash object, except
|
||||
# for MD5 and SHA1, which are hashlib objects. (But test any .new()
|
||||
# method that does exist.)
|
||||
if self.hashmod.__name__ not in ('Crypto.Hash.MD5', 'Crypto.Hash.SHA1') or hasattr(h, 'new'):
|
||||
h2 = h.new()
|
||||
h2.update(self.input)
|
||||
out5 = binascii.b2a_hex(h2.digest())
|
||||
self.assertEqual(self.expected, out5)
|
||||
|
||||
# Verify that Crypto.Hash.new(h) produces a fresh hash object
|
||||
h3 = Crypto.Hash.new(h)
|
||||
h3.update(self.input)
|
||||
out6 = binascii.b2a_hex(h3.digest())
|
||||
self.assertEqual(self.expected, out6)
|
||||
|
||||
if hasattr(h, 'name'):
|
||||
# Verify that Crypto.Hash.new(h.name) produces a fresh hash object
|
||||
h4 = Crypto.Hash.new(h.name)
|
||||
h4.update(self.input)
|
||||
out7 = binascii.b2a_hex(h4.digest())
|
||||
self.assertEqual(self.expected, out7)
|
||||
|
||||
class HashTestOID(unittest.TestCase):
|
||||
def __init__(self, hashmod, oid):
|
||||
unittest.TestCase.__init__(self)
|
||||
self.hashmod = hashmod
|
||||
self.oid = oid
|
||||
|
||||
def runTest(self):
|
||||
from Crypto.Signature import PKCS1_v1_5
|
||||
h = self.hashmod.new()
|
||||
self.assertEqual(PKCS1_v1_5._HASH_OIDS[h.name], self.oid)
|
||||
|
||||
class HashDocStringTest(unittest.TestCase):
|
||||
def __init__(self, hashmod):
|
||||
unittest.TestCase.__init__(self)
|
||||
self.hashmod = hashmod
|
||||
|
||||
def runTest(self):
|
||||
docstring = self.hashmod.__doc__
|
||||
self.assert_(hasattr(self.hashmod, '__doc__'))
|
||||
self.assert_(isinstance(self.hashmod.__doc__, str))
|
||||
|
||||
class GenericHashConstructorTest(unittest.TestCase):
|
||||
def __init__(self, hashmod):
|
||||
unittest.TestCase.__init__(self)
|
||||
self.hashmod = hashmod
|
||||
|
||||
def runTest(self):
|
||||
obj1 = self.hashmod.new("foo")
|
||||
obj2 = self.hashmod.new()
|
||||
obj3 = Crypto.Hash.new(obj1.name, "foo")
|
||||
obj4 = Crypto.Hash.new(obj1.name)
|
||||
obj5 = Crypto.Hash.new(obj1, "foo")
|
||||
obj6 = Crypto.Hash.new(obj1)
|
||||
self.assert_(isinstance(self.hashmod, obj1))
|
||||
self.assert_(isinstance(self.hashmod, obj2))
|
||||
self.assert_(isinstance(self.hashmod, obj3))
|
||||
self.assert_(isinstance(self.hashmod, obj4))
|
||||
self.assert_(isinstance(self.hashmod, obj5))
|
||||
self.assert_(isinstance(self.hashmod, obj6))
|
||||
|
||||
class MACSelfTest(unittest.TestCase):
|
||||
|
||||
def __init__(self, module, description, result, input, key, params):
|
||||
unittest.TestCase.__init__(self)
|
||||
self.module = module
|
||||
self.result = result
|
||||
self.input = input
|
||||
self.key = key
|
||||
self.params = params
|
||||
self.description = description
|
||||
|
||||
def shortDescription(self):
|
||||
return self.description
|
||||
|
||||
def runTest(self):
|
||||
key = binascii.a2b_hex(b(self.key))
|
||||
data = binascii.a2b_hex(b(self.input))
|
||||
|
||||
# Strip whitespace from the expected string (which should be in lowercase-hex)
|
||||
expected = b("".join(self.result.split()))
|
||||
|
||||
h = self.module.new(key, **self.params)
|
||||
h.update(data)
|
||||
out1_bin = h.digest()
|
||||
out1 = binascii.b2a_hex(h.digest())
|
||||
out2 = h.hexdigest()
|
||||
|
||||
# Verify that correct MAC does not raise any exception
|
||||
h.hexverify(out1)
|
||||
h.verify(out1_bin)
|
||||
|
||||
# Verify that incorrect MAC does raise ValueError exception
|
||||
wrong_mac = strxor_c(out1_bin, 255)
|
||||
self.assertRaises(ValueError, h.verify, wrong_mac)
|
||||
self.assertRaises(ValueError, h.hexverify, "4556")
|
||||
|
||||
h = self.module.new(key, data, **self.params)
|
||||
|
||||
out3 = h.hexdigest()
|
||||
out4 = binascii.b2a_hex(h.digest())
|
||||
|
||||
# Test .copy()
|
||||
h2 = h.copy()
|
||||
h.update(b("blah blah blah")) # Corrupt the original hash object
|
||||
out5 = binascii.b2a_hex(h2.digest()) # The copied hash object should return the correct result
|
||||
|
||||
# PY3K: Check that hexdigest() returns str and digest() returns bytes
|
||||
if sys.version_info[0] > 2:
|
||||
self.assertTrue(isinstance(h.digest(), type(b(""))))
|
||||
self.assertTrue(isinstance(h.hexdigest(), type("")))
|
||||
|
||||
# PY3K: Check that .hexverify() accepts bytes or str
|
||||
if sys.version_info[0] > 2:
|
||||
h.hexverify(h.hexdigest())
|
||||
h.hexverify(h.hexdigest().encode('ascii'))
|
||||
|
||||
# PY3K: hexdigest() should return str, and digest() should return bytes
|
||||
self.assertEqual(expected, out1)
|
||||
if sys.version_info[0] == 2:
|
||||
self.assertEqual(expected, out2)
|
||||
self.assertEqual(expected, out3)
|
||||
else:
|
||||
self.assertEqual(expected.decode(), out2)
|
||||
self.assertEqual(expected.decode(), out3)
|
||||
self.assertEqual(expected, out4)
|
||||
self.assertEqual(expected, out5)
|
||||
|
||||
def make_hash_tests(module, module_name, test_data, digest_size, oid=None):
|
||||
tests = []
|
||||
for i in range(len(test_data)):
|
||||
row = test_data[i]
|
||||
(expected, input) = map(b,row[0:2])
|
||||
if len(row) < 3:
|
||||
description = repr(input)
|
||||
else:
|
||||
description = row[2].encode('latin-1')
|
||||
name = "%s #%d: %s" % (module_name, i+1, description)
|
||||
tests.append(HashSelfTest(module, name, expected, input))
|
||||
name = "%s #%d: digest_size" % (module_name, i+1)
|
||||
tests.append(HashDigestSizeSelfTest(module, name, digest_size))
|
||||
if oid is not None:
|
||||
tests.append(HashTestOID(module, oid))
|
||||
tests.append(HashDocStringTest(module))
|
||||
if getattr(module, 'name', None) is not None:
|
||||
tests.append(GenericHashConstructorTest(module))
|
||||
return tests
|
||||
|
||||
def make_mac_tests(module, module_name, test_data):
|
||||
tests = []
|
||||
for i in range(len(test_data)):
|
||||
row = test_data[i]
|
||||
(key, data, results, description, params) = row
|
||||
name = "%s #%d: %s" % (module_name, i+1, description)
|
||||
tests.append(MACSelfTest(module, name, results, data, key, params))
|
||||
return tests
|
||||
|
||||
# vim:set ts=4 sw=4 sts=4 expandtab:
|
249
modules/Crypto/SelfTest/Hash/test_CMAC.py
Normal file
249
modules/Crypto/SelfTest/Hash/test_CMAC.py
Normal file
@ -0,0 +1,249 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# SelfTest/Hash/CMAC.py: Self-test for the CMAC module
|
||||
#
|
||||
# ===================================================================
|
||||
# The contents of this file are dedicated to the public domain. To
|
||||
# the extent that dedication to the public domain is not available,
|
||||
# everyone is granted a worldwide, perpetual, royalty-free,
|
||||
# non-exclusive license to exercise all rights associated with the
|
||||
# contents of this file for any purpose whatsoever.
|
||||
# No rights are reserved.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
# SOFTWARE.
|
||||
# ===================================================================
|
||||
|
||||
"""Self-test suite for Crypto.Hash.CMAC"""
|
||||
|
||||
import sys
|
||||
if sys.version_info[0] == 2 and sys.version_info[1] == 1:
|
||||
from Crypto.Util.py21compat import *
|
||||
from Crypto.Util.py3compat import *
|
||||
|
||||
from common import dict
|
||||
|
||||
from Crypto.Hash import CMAC
|
||||
from Crypto.Cipher import AES, DES3
|
||||
|
||||
# This is a list of (key, data, result, description, module) tuples.
|
||||
test_data = [
|
||||
|
||||
## Test vectors from RFC 4493 ##
|
||||
## The are also in NIST SP 800 38B D.2 ##
|
||||
( '2b7e151628aed2a6abf7158809cf4f3c',
|
||||
'',
|
||||
'bb1d6929e95937287fa37d129b756746',
|
||||
'RFC 4493 #1',
|
||||
AES
|
||||
),
|
||||
|
||||
( '2b7e151628aed2a6abf7158809cf4f3c',
|
||||
'6bc1bee22e409f96e93d7e117393172a',
|
||||
'070a16b46b4d4144f79bdd9dd04a287c',
|
||||
'RFC 4493 #2',
|
||||
AES
|
||||
),
|
||||
|
||||
( '2b7e151628aed2a6abf7158809cf4f3c',
|
||||
'6bc1bee22e409f96e93d7e117393172a'+
|
||||
'ae2d8a571e03ac9c9eb76fac45af8e51'+
|
||||
'30c81c46a35ce411',
|
||||
'dfa66747de9ae63030ca32611497c827',
|
||||
'RFC 4493 #3',
|
||||
AES
|
||||
),
|
||||
|
||||
( '2b7e151628aed2a6abf7158809cf4f3c',
|
||||
'6bc1bee22e409f96e93d7e117393172a'+
|
||||
'ae2d8a571e03ac9c9eb76fac45af8e51'+
|
||||
'30c81c46a35ce411e5fbc1191a0a52ef'+
|
||||
'f69f2445df4f9b17ad2b417be66c3710',
|
||||
'51f0bebf7e3b9d92fc49741779363cfe',
|
||||
'RFC 4493 #4',
|
||||
AES
|
||||
),
|
||||
|
||||
## The rest of Appendix D of NIST SP 800 38B
|
||||
## was not totally correct.
|
||||
## Values in Examples 14, 15, 18, and 19 were wrong.
|
||||
## The updated test values are published in:
|
||||
## http://csrc.nist.gov/publications/nistpubs/800-38B/Updated_CMAC_Examples.pdf
|
||||
|
||||
( '8e73b0f7da0e6452c810f32b809079e5'+
|
||||
'62f8ead2522c6b7b',
|
||||
'',
|
||||
'd17ddf46adaacde531cac483de7a9367',
|
||||
'NIST SP 800 38B D.2 Example 5',
|
||||
AES
|
||||
),
|
||||
|
||||
( '8e73b0f7da0e6452c810f32b809079e5'+
|
||||
'62f8ead2522c6b7b',
|
||||
'6bc1bee22e409f96e93d7e117393172a',
|
||||
'9e99a7bf31e710900662f65e617c5184',
|
||||
'NIST SP 800 38B D.2 Example 6',
|
||||
AES
|
||||
),
|
||||
|
||||
( '8e73b0f7da0e6452c810f32b809079e5'+
|
||||
'62f8ead2522c6b7b',
|
||||
'6bc1bee22e409f96e93d7e117393172a'+
|
||||
'ae2d8a571e03ac9c9eb76fac45af8e51'+
|
||||
'30c81c46a35ce411',
|
||||
'8a1de5be2eb31aad089a82e6ee908b0e',
|
||||
'NIST SP 800 38B D.2 Example 7',
|
||||
AES
|
||||
),
|
||||
|
||||
( '8e73b0f7da0e6452c810f32b809079e5'+
|
||||
'62f8ead2522c6b7b',
|
||||
'6bc1bee22e409f96e93d7e117393172a'+
|
||||
'ae2d8a571e03ac9c9eb76fac45af8e51'+
|
||||
'30c81c46a35ce411e5fbc1191a0a52ef'+
|
||||
'f69f2445df4f9b17ad2b417be66c3710',
|
||||
'a1d5df0eed790f794d77589659f39a11',
|
||||
'NIST SP 800 38B D.2 Example 8',
|
||||
AES
|
||||
),
|
||||
|
||||
( '603deb1015ca71be2b73aef0857d7781'+
|
||||
'1f352c073b6108d72d9810a30914dff4',
|
||||
'',
|
||||
'028962f61b7bf89efc6b551f4667d983',
|
||||
'NIST SP 800 38B D.3 Example 9',
|
||||
AES
|
||||
),
|
||||
|
||||
( '603deb1015ca71be2b73aef0857d7781'+
|
||||
'1f352c073b6108d72d9810a30914dff4',
|
||||
'6bc1bee22e409f96e93d7e117393172a',
|
||||
'28a7023f452e8f82bd4bf28d8c37c35c',
|
||||
'NIST SP 800 38B D.3 Example 10',
|
||||
AES
|
||||
),
|
||||
|
||||
( '603deb1015ca71be2b73aef0857d7781'+
|
||||
'1f352c073b6108d72d9810a30914dff4',
|
||||
'6bc1bee22e409f96e93d7e117393172a'+
|
||||
'ae2d8a571e03ac9c9eb76fac45af8e51'+
|
||||
'30c81c46a35ce411',
|
||||
'aaf3d8f1de5640c232f5b169b9c911e6',
|
||||
'NIST SP 800 38B D.3 Example 11',
|
||||
AES
|
||||
),
|
||||
|
||||
( '603deb1015ca71be2b73aef0857d7781'+
|
||||
'1f352c073b6108d72d9810a30914dff4',
|
||||
'6bc1bee22e409f96e93d7e117393172a'+
|
||||
'ae2d8a571e03ac9c9eb76fac45af8e51'+
|
||||
'30c81c46a35ce411e5fbc1191a0a52ef'+
|
||||
'f69f2445df4f9b17ad2b417be66c3710',
|
||||
'e1992190549f6ed5696a2c056c315410',
|
||||
'NIST SP 800 38B D.3 Example 12',
|
||||
AES
|
||||
),
|
||||
|
||||
( '8aa83bf8cbda1062'+
|
||||
'0bc1bf19fbb6cd58'+
|
||||
'bc313d4a371ca8b5',
|
||||
'',
|
||||
'b7a688e122ffaf95',
|
||||
'NIST SP 800 38B D.4 Example 13',
|
||||
DES3
|
||||
),
|
||||
|
||||
( '8aa83bf8cbda1062'+
|
||||
'0bc1bf19fbb6cd58'+
|
||||
'bc313d4a371ca8b5',
|
||||
'6bc1bee22e409f96',
|
||||
'8e8f293136283797',
|
||||
'NIST SP 800 38B D.4 Example 14',
|
||||
DES3
|
||||
),
|
||||
|
||||
( '8aa83bf8cbda1062'+
|
||||
'0bc1bf19fbb6cd58'+
|
||||
'bc313d4a371ca8b5',
|
||||
'6bc1bee22e409f96'+
|
||||
'e93d7e117393172a'+
|
||||
'ae2d8a57',
|
||||
'743ddbe0ce2dc2ed',
|
||||
'NIST SP 800 38B D.4 Example 15',
|
||||
DES3
|
||||
),
|
||||
|
||||
( '8aa83bf8cbda1062'+
|
||||
'0bc1bf19fbb6cd58'+
|
||||
'bc313d4a371ca8b5',
|
||||
'6bc1bee22e409f96'+
|
||||
'e93d7e117393172a'+
|
||||
'ae2d8a571e03ac9c'+
|
||||
'9eb76fac45af8e51',
|
||||
'33e6b1092400eae5',
|
||||
'NIST SP 800 38B D.4 Example 16',
|
||||
DES3
|
||||
),
|
||||
|
||||
( '4cf15134a2850dd5'+
|
||||
'8a3d10ba80570d38',
|
||||
'',
|
||||
'bd2ebf9a3ba00361',
|
||||
'NIST SP 800 38B D.7 Example 17',
|
||||
DES3
|
||||
),
|
||||
|
||||
( '4cf15134a2850dd5'+
|
||||
'8a3d10ba80570d38',
|
||||
'6bc1bee22e409f96',
|
||||
'4ff2ab813c53ce83',
|
||||
'NIST SP 800 38B D.7 Example 18',
|
||||
DES3
|
||||
),
|
||||
|
||||
( '4cf15134a2850dd5'+
|
||||
'8a3d10ba80570d38',
|
||||
'6bc1bee22e409f96'+
|
||||
'e93d7e117393172a'+
|
||||
'ae2d8a57',
|
||||
'62dd1b471902bd4e',
|
||||
'NIST SP 800 38B D.7 Example 19',
|
||||
DES3
|
||||
),
|
||||
|
||||
( '4cf15134a2850dd5'+
|
||||
'8a3d10ba80570d38',
|
||||
'6bc1bee22e409f96'+
|
||||
'e93d7e117393172a'+
|
||||
'ae2d8a571e03ac9c'+
|
||||
'9eb76fac45af8e51',
|
||||
'31b1e431dabc4eb8',
|
||||
'NIST SP 800 38B D.7 Example 20',
|
||||
DES3
|
||||
),
|
||||
|
||||
]
|
||||
|
||||
def get_tests(config={}):
|
||||
global test_data
|
||||
from common import make_mac_tests
|
||||
|
||||
# Add new() parameters to the back of each test vector
|
||||
params_test_data = []
|
||||
for row in test_data:
|
||||
t = list(row)
|
||||
t[4] = dict(ciphermod=t[4])
|
||||
params_test_data.append(t)
|
||||
|
||||
return make_mac_tests(CMAC, "CMAC", params_test_data)
|
||||
|
||||
if __name__ == '__main__':
|
||||
import unittest
|
||||
suite = lambda: unittest.TestSuite(get_tests())
|
||||
unittest.main(defaultTest='suite')
|
233
modules/Crypto/SelfTest/Hash/test_HMAC.py
Normal file
233
modules/Crypto/SelfTest/Hash/test_HMAC.py
Normal file
@ -0,0 +1,233 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# SelfTest/Hash/HMAC.py: Self-test for the HMAC module
|
||||
#
|
||||
# Written in 2008 by Dwayne C. Litzenberger <dlitz@dlitz.net>
|
||||
#
|
||||
# ===================================================================
|
||||
# The contents of this file are dedicated to the public domain. To
|
||||
# the extent that dedication to the public domain is not available,
|
||||
# everyone is granted a worldwide, perpetual, royalty-free,
|
||||
# non-exclusive license to exercise all rights associated with the
|
||||
# contents of this file for any purpose whatsoever.
|
||||
# No rights are reserved.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
# SOFTWARE.
|
||||
# ===================================================================
|
||||
|
||||
"""Self-test suite for Crypto.Hash.HMAC"""
|
||||
|
||||
__revision__ = "$Id$"
|
||||
|
||||
from common import dict # For compatibility with Python 2.1 and 2.2
|
||||
from Crypto.Util.py3compat import *
|
||||
|
||||
from Crypto.Hash import MD5, SHA1, SHA224, SHA256, SHA384, SHA512, HMAC
|
||||
|
||||
default_hash = None
|
||||
|
||||
# This is a list of (key, data, results, description) tuples.
|
||||
test_data = [
|
||||
## Test vectors from RFC 2202 ##
|
||||
# Test that the default hashmod is MD5
|
||||
('0b' * 16,
|
||||
'4869205468657265',
|
||||
dict(default_hash='9294727a3638bb1c13f48ef8158bfc9d'),
|
||||
'default-is-MD5'),
|
||||
|
||||
# Test case 1 (MD5)
|
||||
('0b' * 16,
|
||||
'4869205468657265',
|
||||
dict(MD5='9294727a3638bb1c13f48ef8158bfc9d'),
|
||||
'RFC 2202 #1-MD5 (HMAC-MD5)'),
|
||||
|
||||
# Test case 1 (SHA1)
|
||||
('0b' * 20,
|
||||
'4869205468657265',
|
||||
dict(SHA1='b617318655057264e28bc0b6fb378c8ef146be00'),
|
||||
'RFC 2202 #1-SHA1 (HMAC-SHA1)'),
|
||||
|
||||
# Test case 2
|
||||
('4a656665',
|
||||
'7768617420646f2079612077616e7420666f72206e6f7468696e673f',
|
||||
dict(MD5='750c783e6ab0b503eaa86e310a5db738',
|
||||
SHA1='effcdf6ae5eb2fa2d27416d5f184df9c259a7c79'),
|
||||
'RFC 2202 #2 (HMAC-MD5/SHA1)'),
|
||||
|
||||
# Test case 3 (MD5)
|
||||
('aa' * 16,
|
||||
'dd' * 50,
|
||||
dict(MD5='56be34521d144c88dbb8c733f0e8b3f6'),
|
||||
'RFC 2202 #3-MD5 (HMAC-MD5)'),
|
||||
|
||||
# Test case 3 (SHA1)
|
||||
('aa' * 20,
|
||||
'dd' * 50,
|
||||
dict(SHA1='125d7342b9ac11cd91a39af48aa17b4f63f175d3'),
|
||||
'RFC 2202 #3-SHA1 (HMAC-SHA1)'),
|
||||
|
||||
# Test case 4
|
||||
('0102030405060708090a0b0c0d0e0f10111213141516171819',
|
||||
'cd' * 50,
|
||||
dict(MD5='697eaf0aca3a3aea3a75164746ffaa79',
|
||||
SHA1='4c9007f4026250c6bc8414f9bf50c86c2d7235da'),
|
||||
'RFC 2202 #4 (HMAC-MD5/SHA1)'),
|
||||
|
||||
# Test case 5 (MD5)
|
||||
('0c' * 16,
|
||||
'546573742057697468205472756e636174696f6e',
|
||||
dict(MD5='56461ef2342edc00f9bab995690efd4c'),
|
||||
'RFC 2202 #5-MD5 (HMAC-MD5)'),
|
||||
|
||||
# Test case 5 (SHA1)
|
||||
# NB: We do not implement hash truncation, so we only test the full hash here.
|
||||
('0c' * 20,
|
||||
'546573742057697468205472756e636174696f6e',
|
||||
dict(SHA1='4c1a03424b55e07fe7f27be1d58bb9324a9a5a04'),
|
||||
'RFC 2202 #5-SHA1 (HMAC-SHA1)'),
|
||||
|
||||
# Test case 6
|
||||
('aa' * 80,
|
||||
'54657374205573696e67204c6172676572205468616e20426c6f636b2d53697a'
|
||||
+ '65204b6579202d2048617368204b6579204669727374',
|
||||
dict(MD5='6b1ab7fe4bd7bf8f0b62e6ce61b9d0cd',
|
||||
SHA1='aa4ae5e15272d00e95705637ce8a3b55ed402112'),
|
||||
'RFC 2202 #6 (HMAC-MD5/SHA1)'),
|
||||
|
||||
# Test case 7
|
||||
('aa' * 80,
|
||||
'54657374205573696e67204c6172676572205468616e20426c6f636b2d53697a'
|
||||
+ '65204b657920616e64204c6172676572205468616e204f6e6520426c6f636b2d'
|
||||
+ '53697a652044617461',
|
||||
dict(MD5='6f630fad67cda0ee1fb1f562db3aa53e',
|
||||
SHA1='e8e99d0f45237d786d6bbaa7965c7808bbff1a91'),
|
||||
'RFC 2202 #7 (HMAC-MD5/SHA1)'),
|
||||
|
||||
## Test vectors from RFC 4231 ##
|
||||
# 4.2. Test Case 1
|
||||
('0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b',
|
||||
'4869205468657265',
|
||||
dict(SHA256='''
|
||||
b0344c61d8db38535ca8afceaf0bf12b
|
||||
881dc200c9833da726e9376c2e32cff7
|
||||
'''),
|
||||
'RFC 4231 #1 (HMAC-SHA256)'),
|
||||
|
||||
# 4.3. Test Case 2 - Test with a key shorter than the length of the HMAC
|
||||
# output.
|
||||
('4a656665',
|
||||
'7768617420646f2079612077616e7420666f72206e6f7468696e673f',
|
||||
dict(SHA256='''
|
||||
5bdcc146bf60754e6a042426089575c7
|
||||
5a003f089d2739839dec58b964ec3843
|
||||
'''),
|
||||
'RFC 4231 #2 (HMAC-SHA256)'),
|
||||
|
||||
# 4.4. Test Case 3 - Test with a combined length of key and data that is
|
||||
# larger than 64 bytes (= block-size of SHA-224 and SHA-256).
|
||||
('aa' * 20,
|
||||
'dd' * 50,
|
||||
dict(SHA256='''
|
||||
773ea91e36800e46854db8ebd09181a7
|
||||
2959098b3ef8c122d9635514ced565fe
|
||||
'''),
|
||||
'RFC 4231 #3 (HMAC-SHA256)'),
|
||||
|
||||
# 4.5. Test Case 4 - Test with a combined length of key and data that is
|
||||
# larger than 64 bytes (= block-size of SHA-224 and SHA-256).
|
||||
('0102030405060708090a0b0c0d0e0f10111213141516171819',
|
||||
'cd' * 50,
|
||||
dict(SHA256='''
|
||||
82558a389a443c0ea4cc819899f2083a
|
||||
85f0faa3e578f8077a2e3ff46729665b
|
||||
'''),
|
||||
'RFC 4231 #4 (HMAC-SHA256)'),
|
||||
|
||||
# 4.6. Test Case 5 - Test with a truncation of output to 128 bits.
|
||||
#
|
||||
# Not included because we do not implement hash truncation.
|
||||
#
|
||||
|
||||
# 4.7. Test Case 6 - Test with a key larger than 128 bytes (= block-size of
|
||||
# SHA-384 and SHA-512).
|
||||
('aa' * 131,
|
||||
'54657374205573696e67204c6172676572205468616e20426c6f636b2d53697a'
|
||||
+ '65204b6579202d2048617368204b6579204669727374',
|
||||
dict(SHA256='''
|
||||
60e431591ee0b67f0d8a26aacbf5b77f
|
||||
8e0bc6213728c5140546040f0ee37f54
|
||||
'''),
|
||||
'RFC 4231 #6 (HMAC-SHA256)'),
|
||||
|
||||
# 4.8. Test Case 7 - Test with a key and data that is larger than 128 bytes
|
||||
# (= block-size of SHA-384 and SHA-512).
|
||||
('aa' * 131,
|
||||
'5468697320697320612074657374207573696e672061206c6172676572207468'
|
||||
+ '616e20626c6f636b2d73697a65206b657920616e642061206c61726765722074'
|
||||
+ '68616e20626c6f636b2d73697a6520646174612e20546865206b6579206e6565'
|
||||
+ '647320746f20626520686173686564206265666f7265206265696e6720757365'
|
||||
+ '642062792074686520484d414320616c676f726974686d2e',
|
||||
dict(SHA256='''
|
||||
9b09ffa71b942fcb27635fbcd5b0e944
|
||||
bfdc63644f0713938a7f51535c3a35e2
|
||||
'''),
|
||||
'RFC 4231 #7 (HMAC-SHA256)'),
|
||||
|
||||
# Test case 8 (SHA224)
|
||||
('4a656665',
|
||||
'7768617420646f2079612077616e74'
|
||||
+ '20666f72206e6f7468696e673f',
|
||||
dict(SHA224='a30e01098bc6dbbf45690f3a7e9e6d0f8bbea2a39e6148008fd05e44'),
|
||||
'RFC 4634 8.4 SHA224 (HMAC-SHA224)'),
|
||||
|
||||
# Test case 9 (SHA384)
|
||||
('4a656665',
|
||||
'7768617420646f2079612077616e74'
|
||||
+ '20666f72206e6f7468696e673f',
|
||||
dict(SHA384='af45d2e376484031617f78d2b58a6b1b9c7ef464f5a01b47e42ec3736322445e8e2240ca5e69e2c78b3239ecfab21649'),
|
||||
'RFC 4634 8.4 SHA384 (HMAC-SHA384)'),
|
||||
|
||||
# Test case 10 (SHA512)
|
||||
('4a656665',
|
||||
'7768617420646f2079612077616e74'
|
||||
+ '20666f72206e6f7468696e673f',
|
||||
dict(SHA512='164b7a7bfcf819e2e395fbe73b56e0a387bd64222e831fd610270cd7ea2505549758bf75c05a994a6d034f65f8f0e6fdcaeab1a34d4a6b4b636e070a38bce737'),
|
||||
'RFC 4634 8.4 SHA512 (HMAC-SHA512)'),
|
||||
|
||||
]
|
||||
|
||||
def get_tests(config={}):
|
||||
global test_data
|
||||
from common import make_mac_tests
|
||||
|
||||
# A test vector contains multiple results, each one for a
|
||||
# different hash algorithm.
|
||||
# Here we expand each test vector into multiple ones,
|
||||
# and add the relevant parameters that will be passed to new()
|
||||
exp_test_data = []
|
||||
for row in test_data:
|
||||
for modname in row[2].keys():
|
||||
t = list(row)
|
||||
t[2] = row[2][modname]
|
||||
try:
|
||||
t.append(dict(digestmod=globals()[modname]))
|
||||
exp_test_data.append(t)
|
||||
except AttributeError:
|
||||
import sys
|
||||
sys.stderr.write("SelfTest: warning: not testing HMAC-%s (not available)\n" % modname)
|
||||
|
||||
return make_mac_tests(HMAC, "HMAC", exp_test_data)
|
||||
|
||||
if __name__ == '__main__':
|
||||
import unittest
|
||||
suite = lambda: unittest.TestSuite(get_tests())
|
||||
unittest.main(defaultTest='suite')
|
||||
|
||||
# vim:set ts=4 sw=4 sts=4 expandtab:
|
64
modules/Crypto/SelfTest/Hash/test_MD2.py
Normal file
64
modules/Crypto/SelfTest/Hash/test_MD2.py
Normal file
@ -0,0 +1,64 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# SelfTest/Hash/MD2.py: Self-test for the MD2 hash function
|
||||
#
|
||||
# Written in 2008 by Dwayne C. Litzenberger <dlitz@dlitz.net>
|
||||
#
|
||||
# ===================================================================
|
||||
# The contents of this file are dedicated to the public domain. To
|
||||
# the extent that dedication to the public domain is not available,
|
||||
# everyone is granted a worldwide, perpetual, royalty-free,
|
||||
# non-exclusive license to exercise all rights associated with the
|
||||
# contents of this file for any purpose whatsoever.
|
||||
# No rights are reserved.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
# SOFTWARE.
|
||||
# ===================================================================
|
||||
|
||||
"""Self-test suite for Crypto.Hash.MD2"""
|
||||
|
||||
__revision__ = "$Id$"
|
||||
|
||||
from Crypto.Util.py3compat import *
|
||||
|
||||
# This is a list of (expected_result, input[, description]) tuples.
|
||||
test_data = [
|
||||
# Test vectors from RFC 1319
|
||||
('8350e5a3e24c153df2275c9f80692773', '', "'' (empty string)"),
|
||||
('32ec01ec4a6dac72c0ab96fb34c0b5d1', 'a'),
|
||||
('da853b0d3f88d99b30283a69e6ded6bb', 'abc'),
|
||||
('ab4f496bfb2a530b219ff33031fe06b0', 'message digest'),
|
||||
|
||||
('4e8ddff3650292ab5a4108c3aa47940b', 'abcdefghijklmnopqrstuvwxyz',
|
||||
'a-z'),
|
||||
|
||||
('da33def2a42df13975352846c30338cd',
|
||||
'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789',
|
||||
'A-Z, a-z, 0-9'),
|
||||
|
||||
('d5976f79d83d3a0dc9806c3c66f3efd8',
|
||||
'1234567890123456789012345678901234567890123456'
|
||||
+ '7890123456789012345678901234567890',
|
||||
"'1234567890' * 8"),
|
||||
]
|
||||
|
||||
def get_tests(config={}):
|
||||
from Crypto.Hash import MD2
|
||||
from common import make_hash_tests
|
||||
return make_hash_tests(MD2, "MD2", test_data,
|
||||
digest_size=16,
|
||||
oid="1.2.840.113549.2.2")
|
||||
|
||||
if __name__ == '__main__':
|
||||
import unittest
|
||||
suite = lambda: unittest.TestSuite(get_tests())
|
||||
unittest.main(defaultTest='suite')
|
||||
|
||||
# vim:set ts=4 sw=4 sts=4 expandtab:
|
64
modules/Crypto/SelfTest/Hash/test_MD4.py
Normal file
64
modules/Crypto/SelfTest/Hash/test_MD4.py
Normal file
@ -0,0 +1,64 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# SelfTest/Hash/MD4.py: Self-test for the MD4 hash function
|
||||
#
|
||||
# Written in 2008 by Dwayne C. Litzenberger <dlitz@dlitz.net>
|
||||
#
|
||||
# ===================================================================
|
||||
# The contents of this file are dedicated to the public domain. To
|
||||
# the extent that dedication to the public domain is not available,
|
||||
# everyone is granted a worldwide, perpetual, royalty-free,
|
||||
# non-exclusive license to exercise all rights associated with the
|
||||
# contents of this file for any purpose whatsoever.
|
||||
# No rights are reserved.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
# SOFTWARE.
|
||||
# ===================================================================
|
||||
|
||||
"""Self-test suite for Crypto.Hash.MD4"""
|
||||
|
||||
__revision__ = "$Id$"
|
||||
|
||||
from Crypto.Util.py3compat import *
|
||||
|
||||
# This is a list of (expected_result, input[, description]) tuples.
|
||||
test_data = [
|
||||
# Test vectors from RFC 1320
|
||||
('31d6cfe0d16ae931b73c59d7e0c089c0', '', "'' (empty string)"),
|
||||
('bde52cb31de33e46245e05fbdbd6fb24', 'a'),
|
||||
('a448017aaf21d8525fc10ae87aa6729d', 'abc'),
|
||||
('d9130a8164549fe818874806e1c7014b', 'message digest'),
|
||||
|
||||
('d79e1c308aa5bbcdeea8ed63df412da9', 'abcdefghijklmnopqrstuvwxyz',
|
||||
'a-z'),
|
||||
|
||||
('043f8582f241db351ce627e153e7f0e4',
|
||||
'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789',
|
||||
'A-Z, a-z, 0-9'),
|
||||
|
||||
('e33b4ddc9c38f2199c3e7b164fcc0536',
|
||||
'1234567890123456789012345678901234567890123456'
|
||||
+ '7890123456789012345678901234567890',
|
||||
"'1234567890' * 8"),
|
||||
]
|
||||
|
||||
def get_tests(config={}):
|
||||
from Crypto.Hash import MD4
|
||||
from common import make_hash_tests
|
||||
return make_hash_tests(MD4, "MD4", test_data,
|
||||
digest_size=16,
|
||||
oid="1.2.840.113549.2.4")
|
||||
|
||||
if __name__ == '__main__':
|
||||
import unittest
|
||||
suite = lambda: unittest.TestSuite(get_tests())
|
||||
unittest.main(defaultTest='suite')
|
||||
|
||||
# vim:set ts=4 sw=4 sts=4 expandtab:
|
64
modules/Crypto/SelfTest/Hash/test_MD5.py
Normal file
64
modules/Crypto/SelfTest/Hash/test_MD5.py
Normal file
@ -0,0 +1,64 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# SelfTest/Hash/MD5.py: Self-test for the MD5 hash function
|
||||
#
|
||||
# Written in 2008 by Dwayne C. Litzenberger <dlitz@dlitz.net>
|
||||
#
|
||||
# ===================================================================
|
||||
# The contents of this file are dedicated to the public domain. To
|
||||
# the extent that dedication to the public domain is not available,
|
||||
# everyone is granted a worldwide, perpetual, royalty-free,
|
||||
# non-exclusive license to exercise all rights associated with the
|
||||
# contents of this file for any purpose whatsoever.
|
||||
# No rights are reserved.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
# SOFTWARE.
|
||||
# ===================================================================
|
||||
|
||||
"""Self-test suite for Crypto.Hash.MD5"""
|
||||
|
||||
__revision__ = "$Id$"
|
||||
|
||||
from Crypto.Util.py3compat import *
|
||||
|
||||
# This is a list of (expected_result, input[, description]) tuples.
|
||||
test_data = [
|
||||
# Test vectors from RFC 1321
|
||||
('d41d8cd98f00b204e9800998ecf8427e', '', "'' (empty string)"),
|
||||
('0cc175b9c0f1b6a831c399e269772661', 'a'),
|
||||
('900150983cd24fb0d6963f7d28e17f72', 'abc'),
|
||||
('f96b697d7cb7938d525a2f31aaf161d0', 'message digest'),
|
||||
|
||||
('c3fcd3d76192e4007dfb496cca67e13b', 'abcdefghijklmnopqrstuvwxyz',
|
||||
'a-z'),
|
||||
|
||||
('d174ab98d277d9f5a5611c2c9f419d9f',
|
||||
'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789',
|
||||
'A-Z, a-z, 0-9'),
|
||||
|
||||
('57edf4a22be3c955ac49da2e2107b67a',
|
||||
'1234567890123456789012345678901234567890123456'
|
||||
+ '7890123456789012345678901234567890',
|
||||
"'1234567890' * 8"),
|
||||
]
|
||||
|
||||
def get_tests(config={}):
|
||||
from Crypto.Hash import MD5
|
||||
from common import make_hash_tests
|
||||
return make_hash_tests(MD5, "MD5", test_data,
|
||||
digest_size=16,
|
||||
oid="1.2.840.113549.2.5")
|
||||
|
||||
if __name__ == '__main__':
|
||||
import unittest
|
||||
suite = lambda: unittest.TestSuite(get_tests())
|
||||
unittest.main(defaultTest='suite')
|
||||
|
||||
# vim:set ts=4 sw=4 sts=4 expandtab:
|
73
modules/Crypto/SelfTest/Hash/test_RIPEMD160.py
Normal file
73
modules/Crypto/SelfTest/Hash/test_RIPEMD160.py
Normal file
@ -0,0 +1,73 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# SelfTest/Hash/test_RIPEMD160.py: Self-test for the RIPEMD-160 hash function
|
||||
#
|
||||
# Written in 2008 by Dwayne C. Litzenberger <dlitz@dlitz.net>
|
||||
#
|
||||
# ===================================================================
|
||||
# The contents of this file are dedicated to the public domain. To
|
||||
# the extent that dedication to the public domain is not available,
|
||||
# everyone is granted a worldwide, perpetual, royalty-free,
|
||||
# non-exclusive license to exercise all rights associated with the
|
||||
# contents of this file for any purpose whatsoever.
|
||||
# No rights are reserved.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
# SOFTWARE.
|
||||
# ===================================================================
|
||||
|
||||
#"""Self-test suite for Crypto.Hash.RIPEMD160"""
|
||||
|
||||
__revision__ = "$Id$"
|
||||
|
||||
from Crypto.Util.py3compat import *
|
||||
|
||||
# This is a list of (expected_result, input[, description]) tuples.
|
||||
test_data = [
|
||||
# Test vectors downloaded 2008-09-12 from
|
||||
# http://homes.esat.kuleuven.be/~bosselae/ripemd160.html
|
||||
('9c1185a5c5e9fc54612808977ee8f548b2258d31', '', "'' (empty string)"),
|
||||
('0bdc9d2d256b3ee9daae347be6f4dc835a467ffe', 'a'),
|
||||
('8eb208f7e05d987a9b044a8e98c6b087f15a0bfc', 'abc'),
|
||||
('5d0689ef49d2fae572b881b123a85ffa21595f36', 'message digest'),
|
||||
|
||||
('f71c27109c692c1b56bbdceb5b9d2865b3708dbc',
|
||||
'abcdefghijklmnopqrstuvwxyz',
|
||||
'a-z'),
|
||||
|
||||
('12a053384a9c0c88e405a06c27dcf49ada62eb2b',
|
||||
'abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq',
|
||||
'abcdbcd...pnopq'),
|
||||
|
||||
('b0e20b6e3116640286ed3a87a5713079b21f5189',
|
||||
'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789',
|
||||
'A-Z, a-z, 0-9'),
|
||||
|
||||
('9b752e45573d4b39f4dbd3323cab82bf63326bfb',
|
||||
'1234567890' * 8,
|
||||
"'1234567890' * 8"),
|
||||
|
||||
('52783243c1697bdbe16d37f97f68f08325dc1528',
|
||||
'a' * 10**6,
|
||||
'"a" * 10**6'),
|
||||
]
|
||||
|
||||
def get_tests(config={}):
|
||||
from Crypto.Hash import RIPEMD160
|
||||
from common import make_hash_tests
|
||||
return make_hash_tests(RIPEMD160, "RIPEMD160", test_data,
|
||||
digest_size=20,
|
||||
oid="1.3.36.3.2.1")
|
||||
|
||||
if __name__ == '__main__':
|
||||
import unittest
|
||||
suite = lambda: unittest.TestSuite(get_tests())
|
||||
unittest.main(defaultTest='suite')
|
||||
|
||||
# vim:set ts=4 sw=4 sts=4 expandtab:
|
64
modules/Crypto/SelfTest/Hash/test_SHA1.py
Normal file
64
modules/Crypto/SelfTest/Hash/test_SHA1.py
Normal file
@ -0,0 +1,64 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# SelfTest/Hash/SHA1.py: Self-test for the SHA-1 hash function
|
||||
#
|
||||
# Written in 2008 by Dwayne C. Litzenberger <dlitz@dlitz.net>
|
||||
#
|
||||
# ===================================================================
|
||||
# The contents of this file are dedicated to the public domain. To
|
||||
# the extent that dedication to the public domain is not available,
|
||||
# everyone is granted a worldwide, perpetual, royalty-free,
|
||||
# non-exclusive license to exercise all rights associated with the
|
||||
# contents of this file for any purpose whatsoever.
|
||||
# No rights are reserved.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
# SOFTWARE.
|
||||
# ===================================================================
|
||||
|
||||
"""Self-test suite for Crypto.Hash.SHA"""
|
||||
|
||||
__revision__ = "$Id$"
|
||||
|
||||
from Crypto.Util.py3compat import *
|
||||
|
||||
# Test vectors from various sources
|
||||
# This is a list of (expected_result, input[, description]) tuples.
|
||||
test_data = [
|
||||
# FIPS PUB 180-2, A.1 - "One-Block Message"
|
||||
('a9993e364706816aba3e25717850c26c9cd0d89d', 'abc'),
|
||||
|
||||
# FIPS PUB 180-2, A.2 - "Multi-Block Message"
|
||||
('84983e441c3bd26ebaae4aa1f95129e5e54670f1',
|
||||
'abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq'),
|
||||
|
||||
# FIPS PUB 180-2, A.3 - "Long Message"
|
||||
# ('34aa973cd4c4daa4f61eeb2bdbad27316534016f',
|
||||
# 'a' * 10**6,
|
||||
# '"a" * 10**6'),
|
||||
|
||||
# RFC 3174: Section 7.3, "TEST4" (multiple of 512 bits)
|
||||
('dea356a2cddd90c7a7ecedc5ebb563934f460452',
|
||||
'01234567' * 80,
|
||||
'"01234567" * 80'),
|
||||
]
|
||||
|
||||
def get_tests(config={}):
|
||||
from Crypto.Hash import SHA1
|
||||
from common import make_hash_tests
|
||||
return make_hash_tests(SHA1, "SHA1", test_data,
|
||||
digest_size=20,
|
||||
oid="1.3.14.3.2.26")
|
||||
|
||||
if __name__ == '__main__':
|
||||
import unittest
|
||||
suite = lambda: unittest.TestSuite(get_tests())
|
||||
unittest.main(defaultTest='suite')
|
||||
|
||||
# vim:set ts=4 sw=4 sts=4 expandtab:
|
65
modules/Crypto/SelfTest/Hash/test_SHA224.py
Normal file
65
modules/Crypto/SelfTest/Hash/test_SHA224.py
Normal file
@ -0,0 +1,65 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# SelfTest/Hash/test_SHA224.py: Self-test for the SHA-224 hash function
|
||||
#
|
||||
# Written in 2008 by Dwayne C. Litzenberger <dlitz@dlitz.net>
|
||||
#
|
||||
# ===================================================================
|
||||
# The contents of this file are dedicated to the public domain. To
|
||||
# the extent that dedication to the public domain is not available,
|
||||
# everyone is granted a worldwide, perpetual, royalty-free,
|
||||
# non-exclusive license to exercise all rights associated with the
|
||||
# contents of this file for any purpose whatsoever.
|
||||
# No rights are reserved.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
# SOFTWARE.
|
||||
# ===================================================================
|
||||
|
||||
"""Self-test suite for Crypto.Hash.SHA224"""
|
||||
|
||||
__revision__ = "$Id$"
|
||||
|
||||
# Test vectors from various sources
|
||||
# This is a list of (expected_result, input[, description]) tuples.
|
||||
test_data = [
|
||||
|
||||
# RFC 3874: Section 3.1, "Test Vector #1
|
||||
('23097d223405d8228642a477bda255b32aadbce4bda0b3f7e36c9da7', 'abc'),
|
||||
|
||||
# RFC 3874: Section 3.2, "Test Vector #2
|
||||
('75388b16512776cc5dba5da1fd890150b0c6455cb4f58b1952522525', 'abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq'),
|
||||
|
||||
# RFC 3874: Section 3.3, "Test Vector #3
|
||||
('20794655980c91d8bbb4c1ea97618a4bf03f42581948b2ee4ee7ad67', 'a' * 10**6, "'a' * 10**6"),
|
||||
|
||||
# Examples from http://de.wikipedia.org/wiki/Secure_Hash_Algorithm
|
||||
('d14a028c2a3a2bc9476102bb288234c415a2b01f828ea62ac5b3e42f', ''),
|
||||
|
||||
('49b08defa65e644cbf8a2dd9270bdededabc741997d1dadd42026d7b',
|
||||
'Franz jagt im komplett verwahrlosten Taxi quer durch Bayern'),
|
||||
|
||||
('58911e7fccf2971a7d07f93162d8bd13568e71aa8fc86fc1fe9043d1',
|
||||
'Frank jagt im komplett verwahrlosten Taxi quer durch Bayern'),
|
||||
|
||||
]
|
||||
|
||||
def get_tests(config={}):
|
||||
from Crypto.Hash import SHA224
|
||||
from common import make_hash_tests
|
||||
return make_hash_tests(SHA224, "SHA224", test_data,
|
||||
digest_size=28,
|
||||
oid='2.16.840.1.101.3.4.2.4')
|
||||
|
||||
if __name__ == '__main__':
|
||||
import unittest
|
||||
suite = lambda: unittest.TestSuite(get_tests())
|
||||
unittest.main(defaultTest='suite')
|
||||
|
||||
# vim:set ts=4 sw=4 sts=4 expandtab:
|
96
modules/Crypto/SelfTest/Hash/test_SHA256.py
Normal file
96
modules/Crypto/SelfTest/Hash/test_SHA256.py
Normal file
@ -0,0 +1,96 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# SelfTest/Hash/test_SHA256.py: Self-test for the SHA-256 hash function
|
||||
#
|
||||
# Written in 2008 by Dwayne C. Litzenberger <dlitz@dlitz.net>
|
||||
#
|
||||
# ===================================================================
|
||||
# The contents of this file are dedicated to the public domain. To
|
||||
# the extent that dedication to the public domain is not available,
|
||||
# everyone is granted a worldwide, perpetual, royalty-free,
|
||||
# non-exclusive license to exercise all rights associated with the
|
||||
# contents of this file for any purpose whatsoever.
|
||||
# No rights are reserved.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
# SOFTWARE.
|
||||
# ===================================================================
|
||||
|
||||
"""Self-test suite for Crypto.Hash.SHA256"""
|
||||
|
||||
__revision__ = "$Id$"
|
||||
|
||||
import unittest
|
||||
from Crypto.Util.py3compat import *
|
||||
|
||||
class LargeSHA256Test(unittest.TestCase):
|
||||
def runTest(self):
|
||||
"""SHA256: 512/520 MiB test"""
|
||||
from Crypto.Hash import SHA256
|
||||
zeros = bchr(0x00) * (1024*1024)
|
||||
|
||||
h = SHA256.new(zeros)
|
||||
for i in xrange(511):
|
||||
h.update(zeros)
|
||||
|
||||
# This test vector is from PyCrypto's old testdata.py file.
|
||||
self.assertEqual('9acca8e8c22201155389f65abbf6bc9723edc7384ead80503839f49dcc56d767', h.hexdigest()) # 512 MiB
|
||||
|
||||
for i in xrange(8):
|
||||
h.update(zeros)
|
||||
|
||||
# This test vector is from PyCrypto's old testdata.py file.
|
||||
self.assertEqual('abf51ad954b246009dfe5a50ecd582fd5b8f1b8b27f30393853c3ef721e7fa6e', h.hexdigest()) # 520 MiB
|
||||
|
||||
def get_tests(config={}):
|
||||
# Test vectors from FIPS PUB 180-2
|
||||
# This is a list of (expected_result, input[, description]) tuples.
|
||||
test_data = [
|
||||
# FIPS PUB 180-2, B.1 - "One-Block Message"
|
||||
('ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad',
|
||||
'abc'),
|
||||
|
||||
# FIPS PUB 180-2, B.2 - "Multi-Block Message"
|
||||
('248d6a61d20638b8e5c026930c3e6039a33ce45964ff2167f6ecedd419db06c1',
|
||||
'abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq'),
|
||||
|
||||
# FIPS PUB 180-2, B.3 - "Long Message"
|
||||
('cdc76e5c9914fb9281a1c7e284d73e67f1809a48a497200e046d39ccc7112cd0',
|
||||
'a' * 10**6,
|
||||
'"a" * 10**6'),
|
||||
|
||||
# Test for an old PyCrypto bug.
|
||||
('f7fd017a3c721ce7ff03f3552c0813adcc48b7f33f07e5e2ba71e23ea393d103',
|
||||
'This message is precisely 55 bytes long, to test a bug.',
|
||||
'Length = 55 (mod 64)'),
|
||||
|
||||
# Example from http://de.wikipedia.org/wiki/Secure_Hash_Algorithm
|
||||
('e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855', ''),
|
||||
|
||||
('d32b568cd1b96d459e7291ebf4b25d007f275c9f13149beeb782fac0716613f8',
|
||||
'Franz jagt im komplett verwahrlosten Taxi quer durch Bayern'),
|
||||
]
|
||||
|
||||
from Crypto.Hash import SHA256
|
||||
from common import make_hash_tests
|
||||
tests = make_hash_tests(SHA256, "SHA256", test_data,
|
||||
digest_size=32,
|
||||
oid="2.16.840.1.101.3.4.2.1")
|
||||
|
||||
if config.get('slow_tests'):
|
||||
tests += [LargeSHA256Test()]
|
||||
|
||||
return tests
|
||||
|
||||
if __name__ == '__main__':
|
||||
import unittest
|
||||
suite = lambda: unittest.TestSuite(get_tests())
|
||||
unittest.main(defaultTest='suite')
|
||||
|
||||
# vim:set ts=4 sw=4 sts=4 expandtab:
|
63
modules/Crypto/SelfTest/Hash/test_SHA384.py
Normal file
63
modules/Crypto/SelfTest/Hash/test_SHA384.py
Normal file
@ -0,0 +1,63 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# SelfTest/Hash/test_SHA.py: Self-test for the SHA-384 hash function
|
||||
#
|
||||
# Written in 2008 by Dwayne C. Litzenberger <dlitz@dlitz.net>
|
||||
#
|
||||
# ===================================================================
|
||||
# The contents of this file are dedicated to the public domain. To
|
||||
# the extent that dedication to the public domain is not available,
|
||||
# everyone is granted a worldwide, perpetual, royalty-free,
|
||||
# non-exclusive license to exercise all rights associated with the
|
||||
# contents of this file for any purpose whatsoever.
|
||||
# No rights are reserved.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
# SOFTWARE.
|
||||
# ===================================================================
|
||||
|
||||
"""Self-test suite for Crypto.Hash.SHA384"""
|
||||
|
||||
__revision__ = "$Id$"
|
||||
|
||||
# Test vectors from various sources
|
||||
# This is a list of (expected_result, input[, description]) tuples.
|
||||
test_data = [
|
||||
|
||||
# RFC 4634: Section Page 8.4, "Test 1"
|
||||
('cb00753f45a35e8bb5a03d699ac65007272c32ab0eded1631a8b605a43ff5bed8086072ba1e7cc2358baeca134c825a7', 'abc'),
|
||||
|
||||
# RFC 4634: Section Page 8.4, "Test 2.2"
|
||||
('09330c33f71147e83d192fc782cd1b4753111b173b3b05d22fa08086e3b0f712fcc7c71a557e2db966c3e9fa91746039', 'abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu'),
|
||||
|
||||
# RFC 4634: Section Page 8.4, "Test 3"
|
||||
('9d0e1809716474cb086e834e310a4a1ced149e9c00f248527972cec5704c2a5b07b8b3dc38ecc4ebae97ddd87f3d8985', 'a' * 10**6, "'a' * 10**6"),
|
||||
|
||||
# Taken from http://de.wikipedia.org/wiki/Secure_Hash_Algorithm
|
||||
('38b060a751ac96384cd9327eb1b1e36a21fdb71114be07434c0cc7bf63f6e1da274edebfe76f65fbd51ad2f14898b95b', ''),
|
||||
|
||||
# Example from http://de.wikipedia.org/wiki/Secure_Hash_Algorithm
|
||||
('71e8383a4cea32d6fd6877495db2ee353542f46fa44bc23100bca48f3366b84e809f0708e81041f427c6d5219a286677',
|
||||
'Franz jagt im komplett verwahrlosten Taxi quer durch Bayern'),
|
||||
|
||||
]
|
||||
|
||||
def get_tests(config={}):
|
||||
from Crypto.Hash import SHA384
|
||||
from common import make_hash_tests
|
||||
return make_hash_tests(SHA384, "SHA384", test_data,
|
||||
digest_size=48,
|
||||
oid='2.16.840.1.101.3.4.2.2')
|
||||
|
||||
if __name__ == '__main__':
|
||||
import unittest
|
||||
suite = lambda: unittest.TestSuite(get_tests())
|
||||
unittest.main(defaultTest='suite')
|
||||
|
||||
# vim:set ts=4 sw=4 sts=4 expandtab:
|
60
modules/Crypto/SelfTest/Hash/test_SHA512.py
Normal file
60
modules/Crypto/SelfTest/Hash/test_SHA512.py
Normal file
@ -0,0 +1,60 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# SelfTest/Hash/test_SHA512.py: Self-test for the SHA-512 hash function
|
||||
#
|
||||
# Written in 2008 by Dwayne C. Litzenberger <dlitz@dlitz.net>
|
||||
#
|
||||
# ===================================================================
|
||||
# The contents of this file are dedicated to the public domain. To
|
||||
# the extent that dedication to the public domain is not available,
|
||||
# everyone is granted a worldwide, perpetual, royalty-free,
|
||||
# non-exclusive license to exercise all rights associated with the
|
||||
# contents of this file for any purpose whatsoever.
|
||||
# No rights are reserved.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
# SOFTWARE.
|
||||
# ===================================================================
|
||||
|
||||
"""Self-test suite for Crypto.Hash.SHA512"""
|
||||
|
||||
__revision__ = "$Id$"
|
||||
|
||||
# Test vectors from various sources
|
||||
# This is a list of (expected_result, input[, description]) tuples.
|
||||
test_data = [
|
||||
|
||||
# RFC 4634: Section Page 8.4, "Test 1"
|
||||
('ddaf35a193617abacc417349ae20413112e6fa4e89a97ea20a9eeee64b55d39a2192992a274fc1a836ba3c23a3feebbd454d4423643ce80e2a9ac94fa54ca49f', 'abc'),
|
||||
|
||||
# RFC 4634: Section Page 8.4, "Test 2.1"
|
||||
('8e959b75dae313da8cf4f72814fc143f8f7779c6eb9f7fa17299aeadb6889018501d289e4900f7e4331b99dec4b5433ac7d329eeb6dd26545e96e55b874be909', 'abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu'),
|
||||
|
||||
# RFC 4634: Section Page 8.4, "Test 3"
|
||||
('e718483d0ce769644e2e42c7bc15b4638e1f98b13b2044285632a803afa973ebde0ff244877ea60a4cb0432ce577c31beb009c5c2c49aa2e4eadb217ad8cc09b', 'a' * 10**6, "'a' * 10**6"),
|
||||
|
||||
# Taken from http://de.wikipedia.org/wiki/Secure_Hash_Algorithm
|
||||
('cf83e1357eefb8bdf1542850d66d8007d620e4050b5715dc83f4a921d36ce9ce47d0d13c5d85f2b0ff8318d2877eec2f63b931bd47417a81a538327af927da3e', ''),
|
||||
|
||||
('af9ed2de700433b803240a552b41b5a472a6ef3fe1431a722b2063c75e9f07451f67a28e37d09cde769424c96aea6f8971389db9e1993d6c565c3c71b855723c', 'Franz jagt im komplett verwahrlosten Taxi quer durch Bayern'),
|
||||
]
|
||||
|
||||
def get_tests(config={}):
|
||||
from Crypto.Hash import SHA512
|
||||
from common import make_hash_tests
|
||||
return make_hash_tests(SHA512, "SHA512", test_data,
|
||||
digest_size=64,
|
||||
oid="2.16.840.1.101.3.4.2.3")
|
||||
|
||||
if __name__ == '__main__':
|
||||
import unittest
|
||||
suite = lambda: unittest.TestSuite(get_tests())
|
||||
unittest.main(defaultTest='suite')
|
||||
|
||||
# vim:set ts=4 sw=4 sts=4 expandtab:
|
34
modules/Crypto/SelfTest/IO/__init__.py
Normal file
34
modules/Crypto/SelfTest/IO/__init__.py
Normal file
@ -0,0 +1,34 @@
|
||||
#
|
||||
# SelfTest/IO/__init__.py: Self-test for input/output module
|
||||
#
|
||||
# ===================================================================
|
||||
# The contents of this file are dedicated to the public domain. To
|
||||
# the extent that dedication to the public domain is not available,
|
||||
# everyone is granted a worldwide, perpetual, royalty-free,
|
||||
# non-exclusive license to exercise all rights associated with the
|
||||
# contents of this file for any purpose whatsoever.
|
||||
# No rights are reserved.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
# SOFTWARE.
|
||||
# ===================================================================
|
||||
|
||||
"""Self-test for I/O"""
|
||||
|
||||
def get_tests(config={}):
|
||||
tests = []
|
||||
from Crypto.SelfTest.IO import test_PKCS8; tests += test_PKCS8.get_tests(config=config)
|
||||
return tests
|
||||
|
||||
if __name__ == '__main__':
|
||||
import unittest
|
||||
suite = lambda: unittest.TestSuite(get_tests())
|
||||
unittest.main(defaultTest='suite')
|
||||
|
||||
|
418
modules/Crypto/SelfTest/IO/test_PKCS8.py
Normal file
418
modules/Crypto/SelfTest/IO/test_PKCS8.py
Normal file
@ -0,0 +1,418 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# SelfTest/PublicKey/test_PKCS8.py: Self-test for the PKCS8 module
|
||||
#
|
||||
# ===================================================================
|
||||
# The contents of this file are dedicated to the public domain. To
|
||||
# the extent that dedication to the public domain is not available,
|
||||
# everyone is granted a worldwide, perpetual, royalty-free,
|
||||
# non-exclusive license to exercise all rights associated with the
|
||||
# contents of this file for any purpose whatsoever.
|
||||
# No rights are reserved.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
# SOFTWARE.
|
||||
# ===================================================================
|
||||
|
||||
"""Self-tests for Crypto.PublicKey.PKCS8 module"""
|
||||
|
||||
__revision__ = "$Id$"
|
||||
|
||||
import unittest
|
||||
import sys
|
||||
from binascii import unhexlify
|
||||
|
||||
from Crypto.Util.py3compat import *
|
||||
if sys.version_info[0] == 2 and sys.version_info[1] == 1:
|
||||
from Crypto.Util.py21compat import *
|
||||
|
||||
from Crypto.IO import PKCS8
|
||||
|
||||
oid_key = '1.2.840.113549.1.1.1'
|
||||
|
||||
# Original RSA key (in DER format)
|
||||
# hexdump -v -e '32/1 "%02x" "\n"' key.der
|
||||
clear_key="""
|
||||
308201ab020100025a00b94a7f7075ab9e79e8196f47be707781e80dd965cf16
|
||||
0c951a870b71783b6aaabbd550c0e65e5a3dfe15b8620009f6d7e5efec42a3f0
|
||||
6fe20faeebb0c356e79cdec6db4dd427e82d8ae4a5b90996227b8ba54ccfc4d2
|
||||
5c08050203010001025a00afa09c70d528299b7552fe766b5d20f9a221d66938
|
||||
c3b68371d48515359863ff96f0978d700e08cd6fd3d8a3f97066fc2e0d5f78eb
|
||||
3a50b8e17ba297b24d1b8e9cdfd18d608668198d724ad15863ef0329195dee89
|
||||
3f039395022d0ebe0518df702a8b25954301ec60a97efdcec8eaa4f2e76ca7e8
|
||||
8dfbc3f7e0bb83f9a0e8dc47c0f8c746e9df6b022d0c9195de13f09b7be1fdd7
|
||||
1f56ae7d973e08bd9fd2c3dfd8936bb05be9cc67bd32d663c7f00d70932a0be3
|
||||
c24f022d0ac334eb6cabf1933633db007b763227b0d9971a9ea36aca8b669ec9
|
||||
4fcf16352f6b3dcae28e4bd6137db4ddd3022d0400a09f15ee7b351a2481cb03
|
||||
09920905c236d09c87afd3022f3afc2a19e3b746672b635238956ee7e6dd62d5
|
||||
022d0cd88ed14fcfbda5bbf0257f700147137bbab9c797af7df866704b889aa3
|
||||
7e2e93df3ff1a0fd3490111dcdbc4c
|
||||
"""
|
||||
|
||||
# Same key as above, wrapped in PKCS#8 but w/o password
|
||||
#
|
||||
# openssl pkcs8 -topk8 -inform DER -nocrypt -in key.der -outform DER -out keyp8.der
|
||||
# hexdump -v -e '32/1 "%02x" "\n"' keyp8.der
|
||||
wrapped_clear_key="""
|
||||
308201c5020100300d06092a864886f70d0101010500048201af308201ab0201
|
||||
00025a00b94a7f7075ab9e79e8196f47be707781e80dd965cf160c951a870b71
|
||||
783b6aaabbd550c0e65e5a3dfe15b8620009f6d7e5efec42a3f06fe20faeebb0
|
||||
c356e79cdec6db4dd427e82d8ae4a5b90996227b8ba54ccfc4d25c0805020301
|
||||
0001025a00afa09c70d528299b7552fe766b5d20f9a221d66938c3b68371d485
|
||||
15359863ff96f0978d700e08cd6fd3d8a3f97066fc2e0d5f78eb3a50b8e17ba2
|
||||
97b24d1b8e9cdfd18d608668198d724ad15863ef0329195dee893f039395022d
|
||||
0ebe0518df702a8b25954301ec60a97efdcec8eaa4f2e76ca7e88dfbc3f7e0bb
|
||||
83f9a0e8dc47c0f8c746e9df6b022d0c9195de13f09b7be1fdd71f56ae7d973e
|
||||
08bd9fd2c3dfd8936bb05be9cc67bd32d663c7f00d70932a0be3c24f022d0ac3
|
||||
34eb6cabf1933633db007b763227b0d9971a9ea36aca8b669ec94fcf16352f6b
|
||||
3dcae28e4bd6137db4ddd3022d0400a09f15ee7b351a2481cb0309920905c236
|
||||
d09c87afd3022f3afc2a19e3b746672b635238956ee7e6dd62d5022d0cd88ed1
|
||||
4fcfbda5bbf0257f700147137bbab9c797af7df866704b889aa37e2e93df3ff1
|
||||
a0fd3490111dcdbc4c
|
||||
"""
|
||||
|
||||
###
|
||||
#
|
||||
# The key above will now be encrypted with different algorithms.
|
||||
# The password is always 'TestTest'.
|
||||
#
|
||||
# Each item in the wrapped_enc_keys list contains:
|
||||
# * wrap algorithm
|
||||
# * iteration count
|
||||
# * Salt
|
||||
# * IV
|
||||
# * Expected result
|
||||
###
|
||||
wrapped_enc_keys = []
|
||||
|
||||
#
|
||||
# openssl pkcs8 -topk8 -passin pass:TestTest -inform DER -in key.der -outform DER -out keyenc.der -v2 des3
|
||||
# hexdump -v -e '32/1 "%02x" "\n"' keyenc.der
|
||||
#
|
||||
wrapped_enc_keys.append((
|
||||
'PBKDF2WithHMAC-SHA1AndDES-EDE3-CBC',
|
||||
2048,
|
||||
"47EA7227D8B22E2F", # IV
|
||||
"E3F7A838AB911A4D", # Salt
|
||||
"""
|
||||
30820216304006092a864886f70d01050d3033301b06092a864886f70d01050c
|
||||
300e0408e3f7a838ab911a4d02020800301406082a864886f70d0307040847ea
|
||||
7227d8b22e2f048201d0ea388b374d2d0e4ceb7a5139f850fdff274884a6e6c0
|
||||
64326e09d00dbba9018834edb5a51a6ae3d1806e6e91eebf33788ce71fee0637
|
||||
a2ebf58859dd32afc644110c390274a6128b50c39b8d907823810ec471bada86
|
||||
6f5b75d8ea04ad310fad2e73621696db8e426cd511ee93ec1714a1a7db45e036
|
||||
4bf20d178d1f16bbb250b32c2d200093169d588de65f7d99aad9ddd0104b44f1
|
||||
326962e1520dfac3c2a800e8a14f678dff2b3d0bb23f69da635bf2a643ac934e
|
||||
219a447d2f4460b67149e860e54f365da130763deefa649c72b0dcd48966a2d3
|
||||
4a477444782e3e66df5a582b07bbb19778a79bd355074ce331f4a82eb966b0c4
|
||||
52a09eab6116f2722064d314ae433b3d6e81d2436e93fdf446112663cde93b87
|
||||
9c8be44beb45f18e2c78fee9b016033f01ecda51b9b142091fa69f65ab784d2c
|
||||
5ad8d34be6f7f1464adfc1e0ef3f7848f40d3bdea4412758f2fcb655c93d8f4d
|
||||
f6fa48fc5aa4b75dd1c017ab79ac9d737233a6d668f5364ccf47786debd37334
|
||||
9c10c9e6efbe78430a61f71c89948aa32cdc3cc7338cf994147819ce7ab23450
|
||||
c8f7d9b94c3bb377d17a3fa204b601526317824b142ff6bc843fa7815ece89c0
|
||||
839573f234dac8d80cc571a045353d61db904a4398d8ef3df5ac
|
||||
"""
|
||||
))
|
||||
|
||||
#
|
||||
# openssl pkcs8 -topk8 -passin pass:TestTest -inform DER -in key.der -outform DER -out keyenc.der
|
||||
# hexdump -v -e '32/1 "%02x" "\n"' keyenc.der
|
||||
#
|
||||
wrapped_enc_keys.append((
|
||||
-1, # pbeWithMD5AndDES-CBC, only decoding is supported
|
||||
-1,
|
||||
"",
|
||||
"",
|
||||
"""
|
||||
308201f1301b06092a864886f70d010503300e0408f9b990c89af1d41b020208
|
||||
00048201d0c6267fe8592903891933d559e71a7ca68b2e39150f19daca0f7921
|
||||
52f97e249d72f670d5140e9150433310ed7c7ee51927693fd39884cb9551cea5
|
||||
a7b746f7edf199f8787d4787a35dad930d7db057b2118851211b645ac8b90fa6
|
||||
b0e7d49ac8567cbd5fff226e87aa9129a0f52c45e9307752e8575c3b0ff756b7
|
||||
31fda6942d15ecb6b27ea19370ccc79773f47891e80d22b440d81259c4c28eac
|
||||
e0ca839524116bcf52d8c566e49a95ddb0e5493437279a770a39fd333f3fca91
|
||||
55884fad0ba5aaf273121f893059d37dd417da7dcfd0d6fa7494968f13b2cc95
|
||||
65633f2c891340193e5ec00e4ee0b0e90b3b93da362a4906360845771ade1754
|
||||
9df79140be5993f3424c012598eadd3e7c7c0b4db2c72cf103d7943a5cf61420
|
||||
93370b9702386c3dd4eb0a47f34b579624a46a108b2d13921fa1b367495fe345
|
||||
6aa128aa70f8ca80ae13eb301e96c380724ce67c54380bbea2316c1faf4d058e
|
||||
b4ca2e23442047606b9bc4b3bf65b432cb271bea4eb35dd3eb360d3be8612a87
|
||||
a50e96a2264490aeabdc07c6e78e5dbf4fe3388726d0e2a228346bf3c2907d68
|
||||
2a6276b22ae883fb30fa611f4e4193e7a08480fcd7db48308bacbd72bf4807aa
|
||||
11fd394859f97d22982f7fe890b2e2a0f7e7ffb693
|
||||
"""
|
||||
))
|
||||
|
||||
#
|
||||
# openssl pkcs8 -topk8 -passin pass:TestTest -inform DER -in key.der
|
||||
# -outform DER -out keyenc.der -v1 PBE-SHA1-RC2-64
|
||||
# hexdump -v -e '32/1 "%02x" "\n"' keyenc.der
|
||||
#
|
||||
wrapped_enc_keys.append((
|
||||
-1, # pbeWithSHA1AndRC2-CBC, only decoding is supported
|
||||
-1,
|
||||
"",
|
||||
"",
|
||||
"""
|
||||
308201f1301b06092a864886f70d01050b300e04083ee943bdae185008020208
|
||||
00048201d0e4614d9371d3ff10ceabc2f6a7a13a0f449f9a714144e46518ea55
|
||||
e3e6f0cde24031d01ef1f37ec40081449ef01914faf45983dde0d2bc496712de
|
||||
8dd15a5527dff4721d9016c13f34fb93e3ce68577e30146266d71b539f854e56
|
||||
753a192cf126ed4812734d86f81884374f1100772f78d0646e9946407637c565
|
||||
d070acab413c55952f7237437f2e48cae7fa0ff8d370de2bf446dd08049a3663
|
||||
d9c813ac197468c02e2b687e7ca994cf7f03f01b6eca87dbfed94502c2094157
|
||||
ea39f73fe4e591df1a68b04d19d9adab90bb9898467c1464ad20bf2b8fb9a5ff
|
||||
d3ec91847d1c67fd768a4b9cfb46572eccc83806601372b6fad0243f58f623b7
|
||||
1c5809dea0feb8278fe27e5560eed8448dc93f5612f546e5dd7c5f6404365eb2
|
||||
5bf3396814367ae8b15c5c432b57eaed1f882c05c7f6517ee9e42b87b7b8d071
|
||||
9d6125d1b52f7b2cca1f6bd5f584334bf90bce1a7d938274cafe27b68e629698
|
||||
b16e27ae528db28593af9adcfccbebb3b9e1f2af5cd5531b51968389caa6c091
|
||||
e7de1f1b96f0d258e54e540d961a7c0ef51fda45d6da5fddd33e9bbfd3a5f8d7
|
||||
d7ab2e971de495cddbc86d38444fee9f0ac097b00adaf7802dabe0cff5b43b45
|
||||
4f26b7b547016f89be52676866189911c53e2f2477"""
|
||||
))
|
||||
|
||||
#
|
||||
# openssl pkcs8 -topk8 -passin pass:TestTest -inform DER -in key.der
|
||||
# -outform DER -out keyenc.der -v1 PBE-MD5-RC2-64
|
||||
# hexdump -v -e '32/1 "%02x" "\n"' keyenc.der
|
||||
#
|
||||
wrapped_enc_keys.append((
|
||||
-1, # pbeWithMD5AndRC2-CBC, only decoding is supported
|
||||
-1,
|
||||
"",
|
||||
"",
|
||||
"""
|
||||
308201f1301b06092a864886f70d010506300e0408f5cd2fee56d9b4b8020208
|
||||
00048201d086454942d6166a19d6b108465bd111e7080911f573d54b1369c676
|
||||
df28600e84936bfec04f91023ff16499e2e07178c340904f12ffa6886ab66228
|
||||
32bf43c2bff5a0ed14e765918cf5fc543ad49566246f7eb3fc044fa5a9c25f40
|
||||
8fc8c8296b91658d3bb1067c0aba008c4fefd9e2bcdbbbd63fdc8085482bccf4
|
||||
f150cec9a084259ad441a017e5d81a1034ef2484696a7a50863836d0eeda45cd
|
||||
8cee8ecabfed703f8d9d4bbdf3a767d32a0ccdc38550ee2928d7fe3fa27eda5b
|
||||
5c7899e75ad55d076d2c2d3c37d6da3d95236081f9671dab9a99afdb1cbc890e
|
||||
332d1a91105d9a8ce08b6027aa07367bd1daec3059cb51f5d896124da16971e4
|
||||
0ca4bcadb06c854bdf39f42dd24174011414e51626d198775eff3449a982df7b
|
||||
ace874e77e045eb6d7c3faef0750792b29a068a6291f7275df1123fac5789c51
|
||||
27ace42836d81633faf9daf38f6787fff0394ea484bbcd465b57d4dbee3cf8df
|
||||
b77d1db287b3a6264c466805be5a4fe85cfbca180699859280f2dd8e2c2c10b5
|
||||
7a7d2ac670c6039d41952fbb0e4f99b560ebe1d020e1b96d02403283819c00cc
|
||||
529c51f0b0101555e4c58002ba3c6e3c12e3fde1aec94382792e96d9666a2b33
|
||||
3dc397b22ecab67ee38a552fec29a1d4ff8719c748"""
|
||||
))
|
||||
|
||||
#
|
||||
# openssl pkcs8 -topk8 -passin pass:TestTest -inform DER -in key.der
|
||||
# -outform DER -out keyenc.der -v1 PBE-SHA1-DES
|
||||
# hexdump -v -e '32/1 "%02x" "\n"' keyenc.der
|
||||
#
|
||||
wrapped_enc_keys.append((
|
||||
-1, # pbeWithSHA1AndDES-CBC, only decoding is supported
|
||||
-1,
|
||||
"",
|
||||
"",
|
||||
"""
|
||||
308201f1301b06092a864886f70d01050a300e04089bacc9cf1e8f734e020208
|
||||
00048201d03e502f3ceafe8fd19ab2939576bfdded26d719b2441db1459688f5
|
||||
9673218b41ec1f739edf1e460bd927bc28470c87b2d4fc8ea02ba17b47a63c49
|
||||
c5c1bee40529dadfd3ef8b4472c730bc136678c78abfb34670ec9d7dcd17ee3f
|
||||
892f93f2629e6e0f4b24ecb9f954069bf722f466dece3913bb6abbd2c471d9a5
|
||||
c5eea89b14aaccda43d30b0dd0f6eb6e9850d9747aa8aa8414c383ad01c374ee
|
||||
26d3552abec9ba22669cc9622ccf2921e3d0c8ecd1a70e861956de0bec6104b5
|
||||
b649ac994970c83f8a9e84b14a7dff7843d4ca3dd4af87cea43b5657e15ae0b5
|
||||
a940ce5047f006ab3596506600724764f23757205fe374fee04911336d655acc
|
||||
03e159ec27789191d1517c4f3f9122f5242d44d25eab8f0658cafb928566ca0e
|
||||
8f6589aa0c0ab13ca7a618008ae3eafd4671ee8fe0b562e70b3623b0e2a16eee
|
||||
97fd388087d2e03530c9fe7db6e52eccc7c48fd701ede35e08922861a9508d12
|
||||
bc8bbf24f0c6bee6e63dbcb489b603d4c4a78ce45bf2eab1d5d10456c42a65a8
|
||||
3a606f4e4b9b46eb13b57f2624b651859d3d2d5192b45dbd5a2ead14ff20ca76
|
||||
48f321309aa56d8c0c4a192b580821cc6c70c75e6f19d1c5414da898ec4dd39d
|
||||
b0eb93d6ba387a80702dfd2db610757ba340f63230
|
||||
"""
|
||||
))
|
||||
|
||||
#
|
||||
# openssl pkcs8 -topk8 -passin pass:TestTest -inform DER -in key.der
|
||||
# -outform DER -out keyenc.der -v2 aes128
|
||||
# hexdump -v -e '32/1 "%02x" "\n"' keyenc.der
|
||||
#
|
||||
wrapped_enc_keys.append((
|
||||
'PBKDF2WithHMAC-SHA1AndAES128-CBC',
|
||||
2048,
|
||||
"4F66EE5D3BCD531FE6EBF4B4E73016B8", # IV
|
||||
"479F25156176C53A", # Salt
|
||||
"""
|
||||
3082021f304906092a864886f70d01050d303c301b06092a864886f70d01050c
|
||||
300e0408479f25156176c53a02020800301d060960864801650304010204104f
|
||||
66ee5d3bcd531fe6ebf4b4e73016b8048201d0e33cfa560423f589d097d21533
|
||||
3b880a5ebac5b2ac58b4e73b0d787aee7764f034fe34ca1d1bd845c0a7c3316f
|
||||
afbfb2129e03dcaf5a5031394206492828dacef1e04639bee5935e0f46114202
|
||||
10bc6c37182f4889be11c5d0486c398f4be952e5740f65de9d8edeb275e2b406
|
||||
e19bc29ad5ebb97fa536344fc3d84c7e755696f12b810898de4e6f069b8a81c8
|
||||
0aab0d45d7d062303aaa4a10c2ce84fdb5a03114039cfe138e38bb15b2ced717
|
||||
93549cdad85e730b14d9e2198b663dfdc8d04a4349eb3de59b076ad40b116d4a
|
||||
25ed917c576bc7c883c95ef0f1180e28fc9981bea069594c309f1aa1b253ceab
|
||||
a2f0313bb1372bcb51a745056be93d77a1f235a762a45e8856512d436b2ca0f7
|
||||
dd60fbed394ba28978d2a2b984b028529d0a58d93aba46c6bbd4ac1e4013cbaa
|
||||
63b00988bc5f11ccc40141c346762d2b28f64435d4be98ec17c1884985e3807e
|
||||
e550db606600993efccf6de0dfc2d2d70b5336a3b018fa415d6bdd59f5777118
|
||||
16806b7bc17c4c7e20ad7176ebfa5a1aa3f6bc10f04b77afd443944642ac9cca
|
||||
d740e082b4a3bbb8bafdd34a0b3c5f2f3c2aceccccdccd092b78994b845bfa61
|
||||
706c3b9df5165ed1dbcbf1244fe41fc9bf993f52f7658e2f87e1baaeacb0f562
|
||||
9d905c
|
||||
"""
|
||||
))
|
||||
|
||||
#
|
||||
# openssl pkcs8 -topk8 -passin pass:TestTest -inform DER -in key.der
|
||||
# -outform DER -out keyenc.der -v2 aes192
|
||||
# hexdump -v -e '32/1 "%02x" "\n"' keyenc.der
|
||||
#
|
||||
wrapped_enc_keys.append((
|
||||
'PBKDF2WithHMAC-SHA1AndAES192-CBC',
|
||||
2048,
|
||||
"5CFC2A4FF7B63201A4A8A5B021148186", # IV
|
||||
"D718541C264944CE", # Salt
|
||||
"""
|
||||
3082021f304906092a864886f70d01050d303c301b06092a864886f70d01050c
|
||||
300e0408d718541c264944ce02020800301d060960864801650304011604105c
|
||||
fc2a4ff7b63201a4a8a5b021148186048201d08e74aaa21b8bcfb15b9790fe95
|
||||
b0e09ddb0f189b6fb1682fdb9f122b804650ddec3c67a1df093a828b3e5fbcc6
|
||||
286abbcc5354c482fd796d972e919ca8a5eba1eaa2293af1d648013ddad72106
|
||||
75622264dfba55dafdda39e338f058f1bdb9846041ffff803797d3fdf3693135
|
||||
8a192729ea8346a7e5e58e925a2e2e4af0818581859e8215d87370eb4194a5ff
|
||||
bae900857d4c591dbc651a241865a817eaede9987c9f9ae4f95c0bf930eea88c
|
||||
4d7596e535ffb7ca369988aba75027a96b9d0bc9c8b0b75f359067fd145a378b
|
||||
02aaa15e9db7a23176224da48a83249005460cc6e429168657f2efa8b1af7537
|
||||
d7d7042f2d683e8271b21d591090963eeb57aea6172f88da139e1614d6a7d1a2
|
||||
1002d5a7a93d6d21156e2b4777f6fc069287a85a1538c46b7722ccde591ab55c
|
||||
630e1ceeb1ac42d1b41f3f654e9da86b5efced43775ea68b2594e50e4005e052
|
||||
0fe753c0898120c2c07265367ff157f6538a1e4080d6f9d1ca9eb51939c9574e
|
||||
f2e4e1e87c1434affd5808563cddd376776dbbf790c6a40028f311a8b58dafa2
|
||||
0970ed34acd6e3e89d063987893b2b9570ddb8cc032b05a723bba9444933ebf3
|
||||
c624204be72f4190e0245197d0cb772bec933fd8442445f9a28bd042d5a3a1e9
|
||||
9a8a07
|
||||
"""
|
||||
))
|
||||
|
||||
#
|
||||
# openssl pkcs8 -topk8 -passin pass:TestTest -inform DER -in key.der
|
||||
# -outform DER -out keyenc.der -v2 aes192
|
||||
# hexdump -v -e '32/1 "%02x" "\n"' keyenc.der
|
||||
#
|
||||
wrapped_enc_keys.append((
|
||||
'PBKDF2WithHMAC-SHA1AndAES256-CBC',
|
||||
2048,
|
||||
"323351F94462AC563E053A056252C2C4", # IV
|
||||
"02A6CD0D12E727B5", # Salt
|
||||
"""
|
||||
3082021f304906092a864886f70d01050d303c301b06092a864886f70d01050c
|
||||
300e040802a6cd0d12e727b502020800301d060960864801650304012a041032
|
||||
3351f94462ac563e053a056252c2c4048201d07f4ef1c7be21aae738a20c5632
|
||||
b8bdbbb9083b6e7f68822267b1f481fd27fdafd61a90660de6e4058790e4c912
|
||||
bf3f319a7c37e6eb3d956daaa143865020d554bf6215e8d7492359aaeef45d6e
|
||||
d85a686ed26c0bf7c18d071d827a86f0b73e1db0c0e7f3d42201544093302a90
|
||||
551ad530692468c47ac15c69500b8ca67d4a17b64d15cecc035ae50b768a36cf
|
||||
07c395afa091e9e6f86f665455fbdc1b21ad79c0908b73da5de75a9b43508d5d
|
||||
44dc97a870cd3cd9f01ca24452e9b11c1b4982946702cfcbfda5b2fcc0203fb5
|
||||
0b52a115760bd635c94d4c95ac2c640ee9a04ffaf6ccff5a8d953dd5d88ca478
|
||||
c377811c521f2191639c643d657a9e364af88bb7c14a356c2b0b4870a23c2f54
|
||||
d41f8157afff731471dccc6058b15e1151bcf84b39b5e622a3a1d65859c912a5
|
||||
591b85e034a1f6af664f030a6bfc8c3d20c70f32b54bcf4da9c2da83cef49cf8
|
||||
e9a74f0e5d358fe50b88acdce6a9db9a7ad61536212fc5f877ebfc7957b8bda4
|
||||
b1582a0f10d515a20ee06cf768db9c977aa6fbdca7540d611ff953012d009dac
|
||||
e8abd059f8e8ffea637c9c7721f817aaf0bb23403e26a0ef0ff0e2037da67d41
|
||||
af728481f53443551a9bff4cea023164e9622b5441a309e1f4bff98e5bf76677
|
||||
8d7cd9
|
||||
"""
|
||||
))
|
||||
|
||||
def txt2bin(inputs):
|
||||
s = b('').join([b(x) for x in inputs if not (x in '\n\r\t ')])
|
||||
return unhexlify(s)
|
||||
|
||||
class Rng:
|
||||
def __init__(self, output):
|
||||
self.output=output
|
||||
self.idx=0
|
||||
def __call__(self, n):
|
||||
output = self.output[self.idx:self.idx+n]
|
||||
self.idx += n
|
||||
return output
|
||||
|
||||
class PKCS8_Decrypt(unittest.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
self.oid_key = oid_key
|
||||
self.clear_key = txt2bin(clear_key)
|
||||
self.wrapped_clear_key = txt2bin(wrapped_clear_key)
|
||||
self.wrapped_enc_keys = []
|
||||
for t in wrapped_enc_keys:
|
||||
self.wrapped_enc_keys.append((
|
||||
t[0],
|
||||
t[1],
|
||||
txt2bin(t[2]),
|
||||
txt2bin(t[3]),
|
||||
txt2bin(t[4])
|
||||
))
|
||||
|
||||
### NO ENCRYTION
|
||||
|
||||
def test1(self):
|
||||
"""Verify unwrapping w/o encryption"""
|
||||
res1, res2, res3 = PKCS8.unwrap(self.wrapped_clear_key)
|
||||
self.assertEqual(res1, self.oid_key)
|
||||
self.assertEqual(res2, self.clear_key)
|
||||
|
||||
def test2(self):
|
||||
"""Verify wrapping w/o encryption"""
|
||||
wrapped = PKCS8.wrap(self.clear_key, self.oid_key)
|
||||
res1, res2, res3 = PKCS8.unwrap(wrapped)
|
||||
self.assertEqual(res1, self.oid_key)
|
||||
self.assertEqual(res2, self.clear_key)
|
||||
|
||||
## ENCRYPTION
|
||||
|
||||
def test3(self):
|
||||
"""Verify unwrapping with encryption"""
|
||||
|
||||
for t in self.wrapped_enc_keys:
|
||||
res1, res2, res3 = PKCS8.unwrap(t[4], b("TestTest"))
|
||||
self.assertEqual(res1, self.oid_key)
|
||||
self.assertEqual(res2, self.clear_key)
|
||||
|
||||
def test4(self):
|
||||
"""Verify wrapping with encryption"""
|
||||
|
||||
for t in self.wrapped_enc_keys:
|
||||
if t[0]==-1:
|
||||
continue
|
||||
rng = Rng(t[2]+t[3])
|
||||
params = { 'iteration_count':t[1] }
|
||||
wrapped = PKCS8.wrap(
|
||||
self.clear_key,
|
||||
self.oid_key,
|
||||
b("TestTest"),
|
||||
protection=t[0],
|
||||
prot_params=params,
|
||||
key_params=None,
|
||||
randfunc=rng)
|
||||
self.assertEqual(wrapped, t[4])
|
||||
|
||||
def get_tests(config={}):
|
||||
from Crypto.SelfTest.st_common import list_test_cases
|
||||
listTests = []
|
||||
listTests += list_test_cases(PKCS8_Decrypt)
|
||||
return listTests
|
||||
|
||||
if __name__ == '__main__':
|
||||
suite = lambda: unittest.TestSuite(get_tests())
|
||||
unittest.main(defaultTest='suite')
|
||||
|
42
modules/Crypto/SelfTest/Protocol/__init__.py
Normal file
42
modules/Crypto/SelfTest/Protocol/__init__.py
Normal file
@ -0,0 +1,42 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# SelfTest/Protocol/__init__.py: Self-tests for Crypto.Protocol
|
||||
#
|
||||
# Written in 2008 by Dwayne C. Litzenberger <dlitz@dlitz.net>
|
||||
#
|
||||
# ===================================================================
|
||||
# The contents of this file are dedicated to the public domain. To
|
||||
# the extent that dedication to the public domain is not available,
|
||||
# everyone is granted a worldwide, perpetual, royalty-free,
|
||||
# non-exclusive license to exercise all rights associated with the
|
||||
# contents of this file for any purpose whatsoever.
|
||||
# No rights are reserved.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
# SOFTWARE.
|
||||
# ===================================================================
|
||||
|
||||
"""Self-test for Crypto.Protocol"""
|
||||
|
||||
__revision__ = "$Id$"
|
||||
|
||||
def get_tests(config={}):
|
||||
tests = []
|
||||
from Crypto.SelfTest.Protocol import test_chaffing; tests += test_chaffing.get_tests(config=config)
|
||||
from Crypto.SelfTest.Protocol import test_rfc1751; tests += test_rfc1751.get_tests(config=config)
|
||||
from Crypto.SelfTest.Protocol import test_AllOrNothing; tests += test_AllOrNothing.get_tests(config=config)
|
||||
from Crypto.SelfTest.Protocol import test_KDF; tests += test_KDF.get_tests(config=config)
|
||||
return tests
|
||||
|
||||
if __name__ == '__main__':
|
||||
import unittest
|
||||
suite = lambda: unittest.TestSuite(get_tests())
|
||||
unittest.main(defaultTest='suite')
|
||||
|
||||
# vim:set ts=4 sw=4 sts=4 expandtab:
|
76
modules/Crypto/SelfTest/Protocol/test_AllOrNothing.py
Normal file
76
modules/Crypto/SelfTest/Protocol/test_AllOrNothing.py
Normal file
@ -0,0 +1,76 @@
|
||||
#
|
||||
# Test script for Crypto.Protocol.AllOrNothing
|
||||
#
|
||||
# Part of the Python Cryptography Toolkit
|
||||
#
|
||||
# Written by Andrew Kuchling and others
|
||||
#
|
||||
# ===================================================================
|
||||
# The contents of this file are dedicated to the public domain. To
|
||||
# the extent that dedication to the public domain is not available,
|
||||
# everyone is granted a worldwide, perpetual, royalty-free,
|
||||
# non-exclusive license to exercise all rights associated with the
|
||||
# contents of this file for any purpose whatsoever.
|
||||
# No rights are reserved.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
# SOFTWARE.
|
||||
# ===================================================================
|
||||
|
||||
__revision__ = "$Id$"
|
||||
|
||||
import unittest
|
||||
from Crypto.Protocol import AllOrNothing
|
||||
from Crypto.Util.py3compat import *
|
||||
|
||||
text = b("""\
|
||||
When in the Course of human events, it becomes necessary for one people to
|
||||
dissolve the political bands which have connected them with another, and to
|
||||
assume among the powers of the earth, the separate and equal station to which
|
||||
the Laws of Nature and of Nature's God entitle them, a decent respect to the
|
||||
opinions of mankind requires that they should declare the causes which impel
|
||||
them to the separation.
|
||||
|
||||
We hold these truths to be self-evident, that all men are created equal, that
|
||||
they are endowed by their Creator with certain unalienable Rights, that among
|
||||
these are Life, Liberty, and the pursuit of Happiness. That to secure these
|
||||
rights, Governments are instituted among Men, deriving their just powers from
|
||||
the consent of the governed. That whenever any Form of Government becomes
|
||||
destructive of these ends, it is the Right of the People to alter or to
|
||||
abolish it, and to institute new Government, laying its foundation on such
|
||||
principles and organizing its powers in such form, as to them shall seem most
|
||||
likely to effect their Safety and Happiness.
|
||||
""")
|
||||
|
||||
class AllOrNothingTest (unittest.TestCase):
|
||||
|
||||
def runTest(self):
|
||||
"Simple test of AllOrNothing"
|
||||
|
||||
from Crypto.Cipher import AES
|
||||
import base64
|
||||
|
||||
# The current AllOrNothing will fail
|
||||
# every so often. Repeat the test
|
||||
# several times to force this.
|
||||
for i in range(50):
|
||||
x = AllOrNothing.AllOrNothing(AES)
|
||||
|
||||
msgblocks = x.digest(text)
|
||||
|
||||
# get a new undigest-only object so there's no leakage
|
||||
y = AllOrNothing.AllOrNothing(AES)
|
||||
text2 = y.undigest(msgblocks)
|
||||
self.assertEqual(text, text2)
|
||||
|
||||
def get_tests(config={}):
|
||||
return [AllOrNothingTest()]
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
158
modules/Crypto/SelfTest/Protocol/test_KDF.py
Normal file
158
modules/Crypto/SelfTest/Protocol/test_KDF.py
Normal file
@ -0,0 +1,158 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# SelfTest/Protocol/test_KDF.py: Self-test for key derivation functions
|
||||
#
|
||||
# ===================================================================
|
||||
# The contents of this file are dedicated to the public domain. To
|
||||
# the extent that dedication to the public domain is not available,
|
||||
# everyone is granted a worldwide, perpetual, royalty-free,
|
||||
# non-exclusive license to exercise all rights associated with the
|
||||
# contents of this file for any purpose whatsoever.
|
||||
# No rights are reserved.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
# SOFTWARE.
|
||||
# ===================================================================
|
||||
|
||||
__revision__ = "$Id$"
|
||||
|
||||
import unittest
|
||||
from binascii import unhexlify
|
||||
|
||||
from Crypto.Util.py3compat import *
|
||||
|
||||
from Crypto.SelfTest.st_common import list_test_cases
|
||||
from Crypto.Hash import SHA1, HMAC
|
||||
from Crypto.Cipher import AES, DES3
|
||||
|
||||
from Crypto.Protocol.KDF import PBKDF1, PBKDF2, _S2V
|
||||
|
||||
def t2b(t): return unhexlify(b(t))
|
||||
|
||||
class PBKDF1_Tests(unittest.TestCase):
|
||||
|
||||
# List of tuples with test data.
|
||||
# Each tuple is made up by:
|
||||
# Item #0: a pass phrase
|
||||
# Item #1: salt (8 bytes encoded in hex)
|
||||
# Item #2: output key length
|
||||
# Item #3: iterations to use
|
||||
# Item #4: expected result (encoded in hex)
|
||||
_testData = (
|
||||
# From http://www.di-mgt.com.au/cryptoKDFs.html#examplespbkdf
|
||||
("password","78578E5A5D63CB06",16,1000,"DC19847E05C64D2FAF10EBFB4A3D2A20"),
|
||||
)
|
||||
|
||||
def test1(self):
|
||||
v = self._testData[0]
|
||||
res = PBKDF1(v[0], t2b(v[1]), v[2], v[3], SHA1)
|
||||
self.assertEqual(res, t2b(v[4]))
|
||||
|
||||
class PBKDF2_Tests(unittest.TestCase):
|
||||
|
||||
# List of tuples with test data.
|
||||
# Each tuple is made up by:
|
||||
# Item #0: a pass phrase
|
||||
# Item #1: salt (encoded in hex)
|
||||
# Item #2: output key length
|
||||
# Item #3: iterations to use
|
||||
# Item #4: expected result (encoded in hex)
|
||||
_testData = (
|
||||
# From http://www.di-mgt.com.au/cryptoKDFs.html#examplespbkdf
|
||||
("password","78578E5A5D63CB06",24,2048,"BFDE6BE94DF7E11DD409BCE20A0255EC327CB936FFE93643"),
|
||||
# From RFC 6050
|
||||
("password","73616c74", 20, 1, "0c60c80f961f0e71f3a9b524af6012062fe037a6"),
|
||||
("password","73616c74", 20, 2, "ea6c014dc72d6f8ccd1ed92ace1d41f0d8de8957"),
|
||||
("password","73616c74", 20, 4096, "4b007901b765489abead49d926f721d065a429c1"),
|
||||
("passwordPASSWORDpassword","73616c7453414c5473616c7453414c5473616c7453414c5473616c7453414c5473616c74",
|
||||
25, 4096, "3d2eec4fe41c849b80c8d83662c0e44a8b291a964cf2f07038"),
|
||||
( 'pass\x00word',"7361006c74",16,4096, "56fa6aa75548099dcc37d7f03425e0c3"),
|
||||
)
|
||||
|
||||
def test1(self):
|
||||
# Test only for HMAC-SHA1 as PRF
|
||||
|
||||
def prf(p,s):
|
||||
return HMAC.new(p,s,SHA1).digest()
|
||||
|
||||
for i in xrange(len(self._testData)):
|
||||
v = self._testData[i]
|
||||
res = PBKDF2(v[0], t2b(v[1]), v[2], v[3])
|
||||
res2 = PBKDF2(v[0], t2b(v[1]), v[2], v[3], prf)
|
||||
self.assertEqual(res, t2b(v[4]))
|
||||
self.assertEqual(res, res2)
|
||||
|
||||
class S2V_Tests(unittest.TestCase):
|
||||
|
||||
# Sequence of test vectors.
|
||||
# Each test vector is made up by:
|
||||
# Item #0: a tuple of strings
|
||||
# Item #1: an AES key
|
||||
# Item #2: the result
|
||||
# Item #3: the cipher module S2V is based on
|
||||
# Everything is hex encoded
|
||||
_testData = [
|
||||
|
||||
# RFC5297, A.1
|
||||
(
|
||||
( '101112131415161718191a1b1c1d1e1f2021222324252627',
|
||||
'112233445566778899aabbccddee' ),
|
||||
'fffefdfcfbfaf9f8f7f6f5f4f3f2f1f0',
|
||||
'85632d07c6e8f37f950acd320a2ecc93',
|
||||
AES
|
||||
),
|
||||
|
||||
# RFC5297, A.2
|
||||
(
|
||||
( '00112233445566778899aabbccddeeffdeaddadadeaddadaffeeddcc'+
|
||||
'bbaa99887766554433221100',
|
||||
'102030405060708090a0',
|
||||
'09f911029d74e35bd84156c5635688c0',
|
||||
'7468697320697320736f6d6520706c61'+
|
||||
'696e7465787420746f20656e63727970'+
|
||||
'74207573696e67205349562d414553'),
|
||||
'7f7e7d7c7b7a79787776757473727170',
|
||||
'7bdb6e3b432667eb06f4d14bff2fbd0f',
|
||||
AES
|
||||
),
|
||||
|
||||
]
|
||||
|
||||
def test1(self):
|
||||
"""Verify correctness of test vector"""
|
||||
for tv in self._testData:
|
||||
s2v = _S2V.new(t2b(tv[1]), tv[3])
|
||||
for s in tv[0]:
|
||||
s2v.update(t2b(s))
|
||||
result = s2v.derive()
|
||||
self.assertEqual(result, t2b(tv[2]))
|
||||
|
||||
def test2(self):
|
||||
"""Verify that no more than 127(AES) and 63(TDES)
|
||||
components are accepted."""
|
||||
key = bchr(0)*16
|
||||
for module in (AES, DES3):
|
||||
s2v = _S2V.new(key, module)
|
||||
max_comps = module.block_size*8-1
|
||||
for i in xrange(max_comps):
|
||||
s2v.update(b("XX"))
|
||||
self.assertRaises(TypeError, s2v.update, b("YY"))
|
||||
|
||||
def get_tests(config={}):
|
||||
tests = []
|
||||
tests += list_test_cases(PBKDF1_Tests)
|
||||
tests += list_test_cases(PBKDF2_Tests)
|
||||
tests += list_test_cases(S2V_Tests)
|
||||
return tests
|
||||
|
||||
if __name__ == '__main__':
|
||||
suite = lambda: unittest.TestSuite(get_tests())
|
||||
unittest.main(defaultTest='suite')
|
||||
|
||||
# vim:set ts=4 sw=4 sts=4
|
74
modules/Crypto/SelfTest/Protocol/test_chaffing.py
Normal file
74
modules/Crypto/SelfTest/Protocol/test_chaffing.py
Normal file
@ -0,0 +1,74 @@
|
||||
#
|
||||
# Test script for Crypto.Protocol.Chaffing
|
||||
#
|
||||
# Part of the Python Cryptography Toolkit
|
||||
#
|
||||
# Written by Andrew Kuchling and others
|
||||
#
|
||||
# ===================================================================
|
||||
# The contents of this file are dedicated to the public domain. To
|
||||
# the extent that dedication to the public domain is not available,
|
||||
# everyone is granted a worldwide, perpetual, royalty-free,
|
||||
# non-exclusive license to exercise all rights associated with the
|
||||
# contents of this file for any purpose whatsoever.
|
||||
# No rights are reserved.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
# SOFTWARE.
|
||||
# ===================================================================
|
||||
|
||||
__revision__ = "$Id$"
|
||||
|
||||
import unittest
|
||||
from Crypto.Protocol import Chaffing
|
||||
|
||||
text = """\
|
||||
When in the Course of human events, it becomes necessary for one people to
|
||||
dissolve the political bands which have connected them with another, and to
|
||||
assume among the powers of the earth, the separate and equal station to which
|
||||
the Laws of Nature and of Nature's God entitle them, a decent respect to the
|
||||
opinions of mankind requires that they should declare the causes which impel
|
||||
them to the separation.
|
||||
|
||||
We hold these truths to be self-evident, that all men are created equal, that
|
||||
they are endowed by their Creator with certain unalienable Rights, that among
|
||||
these are Life, Liberty, and the pursuit of Happiness. That to secure these
|
||||
rights, Governments are instituted among Men, deriving their just powers from
|
||||
the consent of the governed. That whenever any Form of Government becomes
|
||||
destructive of these ends, it is the Right of the People to alter or to
|
||||
abolish it, and to institute new Government, laying its foundation on such
|
||||
principles and organizing its powers in such form, as to them shall seem most
|
||||
likely to effect their Safety and Happiness.
|
||||
"""
|
||||
|
||||
class ChaffingTest (unittest.TestCase):
|
||||
|
||||
def runTest(self):
|
||||
"Simple tests of chaffing and winnowing"
|
||||
# Test constructors
|
||||
Chaffing.Chaff()
|
||||
Chaffing.Chaff(0.5, 1)
|
||||
self.assertRaises(ValueError, Chaffing.Chaff, factor=-1)
|
||||
self.assertRaises(ValueError, Chaffing.Chaff, blocksper=-1)
|
||||
|
||||
data = [(1, 'data1', 'data1'), (2, 'data2', 'data2')]
|
||||
c = Chaffing.Chaff(1.0, 1)
|
||||
c.chaff(data)
|
||||
chaff = c.chaff(data)
|
||||
self.assertEqual(len(chaff), 4)
|
||||
|
||||
c = Chaffing.Chaff(0.0, 1)
|
||||
chaff = c.chaff(data)
|
||||
self.assertEqual(len(chaff), 2)
|
||||
|
||||
def get_tests(config={}):
|
||||
return [ChaffingTest()]
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
62
modules/Crypto/SelfTest/Protocol/test_rfc1751.py
Normal file
62
modules/Crypto/SelfTest/Protocol/test_rfc1751.py
Normal file
@ -0,0 +1,62 @@
|
||||
#
|
||||
# Test script for Crypto.Util.RFC1751.
|
||||
#
|
||||
# Part of the Python Cryptography Toolkit
|
||||
#
|
||||
# Written by Andrew Kuchling and others
|
||||
#
|
||||
# ===================================================================
|
||||
# The contents of this file are dedicated to the public domain. To
|
||||
# the extent that dedication to the public domain is not available,
|
||||
# everyone is granted a worldwide, perpetual, royalty-free,
|
||||
# non-exclusive license to exercise all rights associated with the
|
||||
# contents of this file for any purpose whatsoever.
|
||||
# No rights are reserved.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
# SOFTWARE.
|
||||
# ===================================================================
|
||||
|
||||
__revision__ = "$Id$"
|
||||
|
||||
import binascii
|
||||
import unittest
|
||||
from Crypto.Util import RFC1751
|
||||
from Crypto.Util.py3compat import *
|
||||
|
||||
test_data = [('EB33F77EE73D4053', 'TIDE ITCH SLOW REIN RULE MOT'),
|
||||
('CCAC2AED591056BE4F90FD441C534766',
|
||||
'RASH BUSH MILK LOOK BAD BRIM AVID GAFF BAIT ROT POD LOVE'),
|
||||
('EFF81F9BFBC65350920CDD7416DE8009',
|
||||
'TROD MUTE TAIL WARM CHAR KONG HAAG CITY BORE O TEAL AWL')
|
||||
]
|
||||
|
||||
class RFC1751Test_k2e (unittest.TestCase):
|
||||
|
||||
def runTest (self):
|
||||
"Check converting keys to English"
|
||||
for key, words in test_data:
|
||||
key=binascii.a2b_hex(b(key))
|
||||
self.assertEqual(RFC1751.key_to_english(key), words)
|
||||
|
||||
class RFC1751Test_e2k (unittest.TestCase):
|
||||
|
||||
def runTest (self):
|
||||
"Check converting English strings to keys"
|
||||
for key, words in test_data:
|
||||
key=binascii.a2b_hex(b(key))
|
||||
self.assertEqual(RFC1751.english_to_key(words), key)
|
||||
|
||||
# class RFC1751Test
|
||||
|
||||
def get_tests(config={}):
|
||||
return [RFC1751Test_k2e(), RFC1751Test_e2k()]
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
50
modules/Crypto/SelfTest/PublicKey/__init__.py
Normal file
50
modules/Crypto/SelfTest/PublicKey/__init__.py
Normal file
@ -0,0 +1,50 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# SelfTest/PublicKey/__init__.py: Self-test for public key crypto
|
||||
#
|
||||
# Written in 2008 by Dwayne C. Litzenberger <dlitz@dlitz.net>
|
||||
#
|
||||
# ===================================================================
|
||||
# The contents of this file are dedicated to the public domain. To
|
||||
# the extent that dedication to the public domain is not available,
|
||||
# everyone is granted a worldwide, perpetual, royalty-free,
|
||||
# non-exclusive license to exercise all rights associated with the
|
||||
# contents of this file for any purpose whatsoever.
|
||||
# No rights are reserved.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
# SOFTWARE.
|
||||
# ===================================================================
|
||||
|
||||
"""Self-test for public-key crypto"""
|
||||
|
||||
__revision__ = "$Id$"
|
||||
|
||||
import os
|
||||
|
||||
def get_tests(config={}):
|
||||
tests = []
|
||||
from Crypto.SelfTest.PublicKey import test_DSA; tests += test_DSA.get_tests(config=config)
|
||||
from Crypto.SelfTest.PublicKey import test_RSA; tests += test_RSA.get_tests(config=config)
|
||||
|
||||
from Crypto.SelfTest.PublicKey import test_import_DSA
|
||||
tests +=test_import_DSA.get_tests(config=config)
|
||||
|
||||
from Crypto.SelfTest.PublicKey import test_import_RSA
|
||||
tests += test_import_RSA.get_tests(config=config)
|
||||
|
||||
from Crypto.SelfTest.PublicKey import test_ElGamal; tests += test_ElGamal.get_tests(config=config)
|
||||
return tests
|
||||
|
||||
if __name__ == '__main__':
|
||||
import unittest
|
||||
suite = lambda: unittest.TestSuite(get_tests())
|
||||
unittest.main(defaultTest='suite')
|
||||
|
||||
# vim:set ts=4 sw=4 sts=4 expandtab:
|
244
modules/Crypto/SelfTest/PublicKey/test_DSA.py
Normal file
244
modules/Crypto/SelfTest/PublicKey/test_DSA.py
Normal file
@ -0,0 +1,244 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# SelfTest/PublicKey/test_DSA.py: Self-test for the DSA primitive
|
||||
#
|
||||
# Written in 2008 by Dwayne C. Litzenberger <dlitz@dlitz.net>
|
||||
#
|
||||
# ===================================================================
|
||||
# The contents of this file are dedicated to the public domain. To
|
||||
# the extent that dedication to the public domain is not available,
|
||||
# everyone is granted a worldwide, perpetual, royalty-free,
|
||||
# non-exclusive license to exercise all rights associated with the
|
||||
# contents of this file for any purpose whatsoever.
|
||||
# No rights are reserved.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
# SOFTWARE.
|
||||
# ===================================================================
|
||||
|
||||
"""Self-test suite for Crypto.PublicKey.DSA"""
|
||||
|
||||
__revision__ = "$Id$"
|
||||
|
||||
import sys
|
||||
import os
|
||||
if sys.version_info[0] == 2 and sys.version_info[1] == 1:
|
||||
from Crypto.Util.py21compat import *
|
||||
from Crypto.Util.py3compat import *
|
||||
|
||||
import unittest
|
||||
from Crypto.SelfTest.st_common import list_test_cases, a2b_hex, b2a_hex
|
||||
|
||||
def _sws(s):
|
||||
"""Remove whitespace from a text or byte string"""
|
||||
if isinstance(s,str):
|
||||
return "".join(s.split())
|
||||
else:
|
||||
return b("").join(s.split())
|
||||
|
||||
class DSATest(unittest.TestCase):
|
||||
# Test vector from "Appendix 5. Example of the DSA" of
|
||||
# "Digital Signature Standard (DSS)",
|
||||
# U.S. Department of Commerce/National Institute of Standards and Technology
|
||||
# FIPS 186-2 (+Change Notice), 2000 January 27.
|
||||
# http://csrc.nist.gov/publications/fips/fips186-2/fips186-2-change1.pdf
|
||||
|
||||
y = _sws("""19131871 d75b1612 a819f29d 78d1b0d7 346f7aa7 7bb62a85
|
||||
9bfd6c56 75da9d21 2d3a36ef 1672ef66 0b8c7c25 5cc0ec74
|
||||
858fba33 f44c0669 9630a76b 030ee333""")
|
||||
|
||||
g = _sws("""626d0278 39ea0a13 413163a5 5b4cb500 299d5522 956cefcb
|
||||
3bff10f3 99ce2c2e 71cb9de5 fa24babf 58e5b795 21925c9c
|
||||
c42e9f6f 464b088c c572af53 e6d78802""")
|
||||
|
||||
p = _sws("""8df2a494 492276aa 3d25759b b06869cb eac0d83a fb8d0cf7
|
||||
cbb8324f 0d7882e5 d0762fc5 b7210eaf c2e9adac 32ab7aac
|
||||
49693dfb f83724c2 ec0736ee 31c80291""")
|
||||
|
||||
q = _sws("""c773218c 737ec8ee 993b4f2d ed30f48e dace915f""")
|
||||
|
||||
x = _sws("""2070b322 3dba372f de1c0ffc 7b2e3b49 8b260614""")
|
||||
|
||||
k = _sws("""358dad57 1462710f 50e254cf 1a376b2b deaadfbf""")
|
||||
k_inverse = _sws("""0d516729 8202e49b 4116ac10 4fc3f415 ae52f917""")
|
||||
m = b2a_hex(b("abc"))
|
||||
m_hash = _sws("""a9993e36 4706816a ba3e2571 7850c26c 9cd0d89d""")
|
||||
r = _sws("""8bac1ab6 6410435c b7181f95 b16ab97c 92b341c0""")
|
||||
s = _sws("""41e2345f 1f56df24 58f426d1 55b4ba2d b6dcd8c8""")
|
||||
|
||||
def setUp(self):
|
||||
global DSA, Random, bytes_to_long, size
|
||||
from Crypto.PublicKey import DSA
|
||||
from Crypto import Random
|
||||
from Crypto.Util.number import bytes_to_long, inverse, size
|
||||
|
||||
self.dsa = DSA
|
||||
|
||||
def test_generate_1arg(self):
|
||||
"""DSA (default implementation) generated key (1 argument)"""
|
||||
dsaObj = self.dsa.generate(1024)
|
||||
self._check_private_key(dsaObj)
|
||||
pub = dsaObj.publickey()
|
||||
self._check_public_key(pub)
|
||||
|
||||
def test_generate_2arg(self):
|
||||
"""DSA (default implementation) generated key (2 arguments)"""
|
||||
dsaObj = self.dsa.generate(1024, Random.new().read)
|
||||
self._check_private_key(dsaObj)
|
||||
pub = dsaObj.publickey()
|
||||
self._check_public_key(pub)
|
||||
|
||||
def test_construct_4tuple(self):
|
||||
"""DSA (default implementation) constructed key (4-tuple)"""
|
||||
(y, g, p, q) = [bytes_to_long(a2b_hex(param)) for param in (self.y, self.g, self.p, self.q)]
|
||||
dsaObj = self.dsa.construct((y, g, p, q))
|
||||
self._test_verification(dsaObj)
|
||||
|
||||
def test_construct_5tuple(self):
|
||||
"""DSA (default implementation) constructed key (5-tuple)"""
|
||||
(y, g, p, q, x) = [bytes_to_long(a2b_hex(param)) for param in (self.y, self.g, self.p, self.q, self.x)]
|
||||
dsaObj = self.dsa.construct((y, g, p, q, x))
|
||||
self._test_signing(dsaObj)
|
||||
self._test_verification(dsaObj)
|
||||
|
||||
def _check_private_key(self, dsaObj):
|
||||
# Check capabilities
|
||||
self.assertEqual(1, dsaObj.has_private())
|
||||
self.assertEqual(1, dsaObj.can_sign())
|
||||
self.assertEqual(0, dsaObj.can_encrypt())
|
||||
self.assertEqual(0, dsaObj.can_blind())
|
||||
|
||||
# Check dsaObj.[ygpqx] -> dsaObj.key.[ygpqx] mapping
|
||||
self.assertEqual(dsaObj.y, dsaObj.key.y)
|
||||
self.assertEqual(dsaObj.g, dsaObj.key.g)
|
||||
self.assertEqual(dsaObj.p, dsaObj.key.p)
|
||||
self.assertEqual(dsaObj.q, dsaObj.key.q)
|
||||
self.assertEqual(dsaObj.x, dsaObj.key.x)
|
||||
|
||||
# Sanity check key data
|
||||
self.assertEqual(1, dsaObj.p > dsaObj.q) # p > q
|
||||
self.assertEqual(160, size(dsaObj.q)) # size(q) == 160 bits
|
||||
self.assertEqual(0, (dsaObj.p - 1) % dsaObj.q) # q is a divisor of p-1
|
||||
self.assertEqual(dsaObj.y, pow(dsaObj.g, dsaObj.x, dsaObj.p)) # y == g**x mod p
|
||||
self.assertEqual(1, 0 < dsaObj.x < dsaObj.q) # 0 < x < q
|
||||
|
||||
def _check_public_key(self, dsaObj):
|
||||
k = a2b_hex(self.k)
|
||||
m_hash = a2b_hex(self.m_hash)
|
||||
|
||||
# Check capabilities
|
||||
self.assertEqual(0, dsaObj.has_private())
|
||||
self.assertEqual(1, dsaObj.can_sign())
|
||||
self.assertEqual(0, dsaObj.can_encrypt())
|
||||
self.assertEqual(0, dsaObj.can_blind())
|
||||
|
||||
# Check dsaObj.[ygpq] -> dsaObj.key.[ygpq] mapping
|
||||
self.assertEqual(dsaObj.y, dsaObj.key.y)
|
||||
self.assertEqual(dsaObj.g, dsaObj.key.g)
|
||||
self.assertEqual(dsaObj.p, dsaObj.key.p)
|
||||
self.assertEqual(dsaObj.q, dsaObj.key.q)
|
||||
|
||||
# Check that private parameters are all missing
|
||||
self.assertEqual(0, hasattr(dsaObj, 'x'))
|
||||
self.assertEqual(0, hasattr(dsaObj.key, 'x'))
|
||||
|
||||
# Sanity check key data
|
||||
self.assertEqual(1, dsaObj.p > dsaObj.q) # p > q
|
||||
self.assertEqual(160, size(dsaObj.q)) # size(q) == 160 bits
|
||||
self.assertEqual(0, (dsaObj.p - 1) % dsaObj.q) # q is a divisor of p-1
|
||||
|
||||
# Public-only key objects should raise an error when .sign() is called
|
||||
self.assertRaises(TypeError, dsaObj.sign, m_hash, k)
|
||||
|
||||
# Check __eq__ and __ne__
|
||||
self.assertEqual(dsaObj.publickey() == dsaObj.publickey(),True) # assert_
|
||||
self.assertEqual(dsaObj.publickey() != dsaObj.publickey(),False) # failIf
|
||||
|
||||
def _test_signing(self, dsaObj):
|
||||
k = a2b_hex(self.k)
|
||||
m_hash = a2b_hex(self.m_hash)
|
||||
r = bytes_to_long(a2b_hex(self.r))
|
||||
s = bytes_to_long(a2b_hex(self.s))
|
||||
(r_out, s_out) = dsaObj.sign(m_hash, k)
|
||||
self.assertEqual((r, s), (r_out, s_out))
|
||||
|
||||
def _test_verification(self, dsaObj):
|
||||
m_hash = a2b_hex(self.m_hash)
|
||||
r = bytes_to_long(a2b_hex(self.r))
|
||||
s = bytes_to_long(a2b_hex(self.s))
|
||||
self.assertEqual(1, dsaObj.verify(m_hash, (r, s)))
|
||||
self.assertEqual(0, dsaObj.verify(m_hash + b("\0"), (r, s)))
|
||||
|
||||
class DSAFastMathTest(DSATest):
|
||||
def setUp(self):
|
||||
DSATest.setUp(self)
|
||||
self.dsa = DSA.DSAImplementation(use_fast_math=True)
|
||||
|
||||
def test_generate_1arg(self):
|
||||
"""DSA (_fastmath implementation) generated key (1 argument)"""
|
||||
DSATest.test_generate_1arg(self)
|
||||
|
||||
def test_generate_2arg(self):
|
||||
"""DSA (_fastmath implementation) generated key (2 arguments)"""
|
||||
DSATest.test_generate_2arg(self)
|
||||
|
||||
def test_construct_4tuple(self):
|
||||
"""DSA (_fastmath implementation) constructed key (4-tuple)"""
|
||||
DSATest.test_construct_4tuple(self)
|
||||
|
||||
def test_construct_5tuple(self):
|
||||
"""DSA (_fastmath implementation) constructed key (5-tuple)"""
|
||||
DSATest.test_construct_5tuple(self)
|
||||
|
||||
class DSASlowMathTest(DSATest):
|
||||
def setUp(self):
|
||||
DSATest.setUp(self)
|
||||
self.dsa = DSA.DSAImplementation(use_fast_math=False)
|
||||
|
||||
def test_generate_1arg(self):
|
||||
"""DSA (_slowmath implementation) generated key (1 argument)"""
|
||||
DSATest.test_generate_1arg(self)
|
||||
|
||||
def test_generate_2arg(self):
|
||||
"""DSA (_slowmath implementation) generated key (2 arguments)"""
|
||||
DSATest.test_generate_2arg(self)
|
||||
|
||||
def test_construct_4tuple(self):
|
||||
"""DSA (_slowmath implementation) constructed key (4-tuple)"""
|
||||
DSATest.test_construct_4tuple(self)
|
||||
|
||||
def test_construct_5tuple(self):
|
||||
"""DSA (_slowmath implementation) constructed key (5-tuple)"""
|
||||
DSATest.test_construct_5tuple(self)
|
||||
|
||||
|
||||
def get_tests(config={}):
|
||||
tests = []
|
||||
tests += list_test_cases(DSATest)
|
||||
try:
|
||||
from Crypto.PublicKey import _fastmath
|
||||
tests += list_test_cases(DSAFastMathTest)
|
||||
except ImportError:
|
||||
from distutils.sysconfig import get_config_var
|
||||
import inspect
|
||||
_fm_path = os.path.normpath(os.path.dirname(os.path.abspath(
|
||||
inspect.getfile(inspect.currentframe())))
|
||||
+"/../../PublicKey/_fastmath"+get_config_var("SO"))
|
||||
if os.path.exists(_fm_path):
|
||||
raise ImportError("While the _fastmath module exists, importing "+
|
||||
"it failed. This may point to the gmp or mpir shared library "+
|
||||
"not being in the path. _fastmath was found at "+_fm_path)
|
||||
tests += list_test_cases(DSASlowMathTest)
|
||||
return tests
|
||||
|
||||
if __name__ == '__main__':
|
||||
suite = lambda: unittest.TestSuite(get_tests())
|
||||
unittest.main(defaultTest='suite')
|
||||
|
||||
# vim:set ts=4 sw=4 sts=4 expandtab:
|
210
modules/Crypto/SelfTest/PublicKey/test_ElGamal.py
Normal file
210
modules/Crypto/SelfTest/PublicKey/test_ElGamal.py
Normal file
@ -0,0 +1,210 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# SelfTest/PublicKey/test_ElGamal.py: Self-test for the ElGamal primitive
|
||||
#
|
||||
# ===================================================================
|
||||
# The contents of this file are dedicated to the public domain. To
|
||||
# the extent that dedication to the public domain is not available,
|
||||
# everyone is granted a worldwide, perpetual, royalty-free,
|
||||
# non-exclusive license to exercise all rights associated with the
|
||||
# contents of this file for any purpose whatsoever.
|
||||
# No rights are reserved.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
# SOFTWARE.
|
||||
# ===================================================================
|
||||
|
||||
"""Self-test suite for Crypto.PublicKey.ElGamal"""
|
||||
|
||||
__revision__ = "$Id$"
|
||||
|
||||
import unittest
|
||||
from Crypto.SelfTest.st_common import list_test_cases, a2b_hex, b2a_hex
|
||||
from Crypto import Random
|
||||
from Crypto.PublicKey import ElGamal
|
||||
from Crypto.Util.number import bytes_to_long
|
||||
from Crypto.Util.py3compat import *
|
||||
|
||||
class ElGamalTest(unittest.TestCase):
|
||||
|
||||
#
|
||||
# Test vectors
|
||||
#
|
||||
# There seem to be no real ElGamal test vectors available in the
|
||||
# public domain. The following test vectors have been generated
|
||||
# with libgcrypt 1.5.0.
|
||||
#
|
||||
# Encryption
|
||||
tve=[
|
||||
{
|
||||
# 256 bits
|
||||
'p' :'BA4CAEAAED8CBE952AFD2126C63EB3B345D65C2A0A73D2A3AD4138B6D09BD933',
|
||||
'g' :'05',
|
||||
'y' :'60D063600ECED7C7C55146020E7A31C4476E9793BEAED420FEC9E77604CAE4EF',
|
||||
'x' :'1D391BA2EE3C37FE1BA175A69B2C73A11238AD77675932',
|
||||
'k' :'F5893C5BAB4131264066F57AB3D8AD89E391A0B68A68A1',
|
||||
'pt' :'48656C6C6F207468657265',
|
||||
'ct1':'32BFD5F487966CEA9E9356715788C491EC515E4ED48B58F0F00971E93AAA5EC7',
|
||||
'ct2':'7BE8FBFF317C93E82FCEF9BD515284BA506603FEA25D01C0CB874A31F315EE68'
|
||||
},
|
||||
|
||||
{
|
||||
# 512 bits
|
||||
'p' :'F1B18AE9F7B4E08FDA9A04832F4E919D89462FD31BF12F92791A93519F75076D6CE3942689CDFF2F344CAFF0F82D01864F69F3AECF566C774CBACF728B81A227',
|
||||
'g' :'07',
|
||||
'y' :'688628C676E4F05D630E1BE39D0066178CA7AA83836B645DE5ADD359B4825A12B02EF4252E4E6FA9BEC1DB0BE90F6D7C8629CABB6E531F472B2664868156E20C',
|
||||
'x' :'14E60B1BDFD33436C0DA8A22FDC14A2CCDBBED0627CE68',
|
||||
'k' :'38DBF14E1F319BDA9BAB33EEEADCAF6B2EA5250577ACE7',
|
||||
'pt' :'48656C6C6F207468657265',
|
||||
'ct1':'290F8530C2CC312EC46178724F196F308AD4C523CEABB001FACB0506BFED676083FE0F27AC688B5C749AB3CB8A80CD6F7094DBA421FB19442F5A413E06A9772B',
|
||||
'ct2':'1D69AAAD1DC50493FB1B8E8721D621D683F3BF1321BE21BC4A43E11B40C9D4D9C80DE3AAC2AB60D31782B16B61112E68220889D53C4C3136EE6F6CE61F8A23A0'
|
||||
}
|
||||
]
|
||||
|
||||
# Signature
|
||||
tvs=[
|
||||
{
|
||||
# 256 bits
|
||||
'p' :'D2F3C41EA66530838A704A48FFAC9334F4701ECE3A97CEE4C69DD01AE7129DD7',
|
||||
'g' :'05',
|
||||
'y' :'C3F9417DC0DAFEA6A05C1D2333B7A95E63B3F4F28CC962254B3256984D1012E7',
|
||||
'x' :'165E4A39BE44D5A2D8B1332D416BC559616F536BC735BB',
|
||||
'k' :'C7F0C794A7EAD726E25A47FF8928013680E73C51DD3D7D99BFDA8F492585928F',
|
||||
'h' :'48656C6C6F207468657265',
|
||||
'sig1':'35CA98133779E2073EF31165AFCDEB764DD54E96ADE851715495F9C635E1E7C2',
|
||||
'sig2':'0135B88B1151279FE5D8078D4FC685EE81177EE9802AB123A73925FC1CB059A7',
|
||||
},
|
||||
{
|
||||
# 512 bits
|
||||
'p' :'E24CF3A4B8A6AF749DCA6D714282FE4AABEEE44A53BB6ED15FBE32B5D3C3EF9CC4124A2ECA331F3C1C1B667ACA3766825217E7B5F9856648D95F05330C6A19CF',
|
||||
'g' :'0B',
|
||||
'y' :'2AD3A1049CA5D4ED207B2431C79A8719BB4073D4A94E450EA6CEE8A760EB07ADB67C0D52C275EE85D7B52789061EE45F2F37D9B2AE522A51C28329766BFE68AC',
|
||||
'x' :'16CBB4F46D9ECCF24FF9F7E63CAA3BD8936341555062AB',
|
||||
'k' :'8A3D89A4E429FD2476D7D717251FB79BF900FFE77444E6BB8299DC3F84D0DD57ABAB50732AE158EA52F5B9E7D8813E81FD9F79470AE22F8F1CF9AEC820A78C69',
|
||||
'h' :'48656C6C6F207468657265',
|
||||
'sig1':'BE001AABAFFF976EC9016198FBFEA14CBEF96B000CCC0063D3324016F9E91FE80D8F9325812ED24DDB2B4D4CF4430B169880B3CE88313B53255BD4EC0378586F',
|
||||
'sig2':'5E266F3F837BA204E3BBB6DBECC0611429D96F8C7CE8F4EFDF9D4CB681C2A954468A357BF4242CEC7418B51DFC081BCD21299EF5B5A0DDEF3A139A1817503DDE',
|
||||
}
|
||||
]
|
||||
|
||||
def test_generate_128(self):
|
||||
self._test_random_key(128)
|
||||
|
||||
def test_generate_512(self):
|
||||
self._test_random_key(512)
|
||||
|
||||
def test_encryption(self):
|
||||
for tv in self.tve:
|
||||
for as_longs in (0,1):
|
||||
d = self.convert_tv(tv, as_longs)
|
||||
key = ElGamal.construct(d['key'])
|
||||
ct = key.encrypt(d['pt'], d['k'])
|
||||
self.assertEquals(ct[0], d['ct1'])
|
||||
self.assertEquals(ct[1], d['ct2'])
|
||||
|
||||
def test_decryption(self):
|
||||
for tv in self.tve:
|
||||
for as_longs in (0,1):
|
||||
d = self.convert_tv(tv, as_longs)
|
||||
key = ElGamal.construct(d['key'])
|
||||
pt = key.decrypt((d['ct1'], d['ct2']))
|
||||
self.assertEquals(pt, d['pt'])
|
||||
|
||||
def test_signing(self):
|
||||
for tv in self.tvs:
|
||||
for as_longs in (0,1):
|
||||
d = self.convert_tv(tv, as_longs)
|
||||
key = ElGamal.construct(d['key'])
|
||||
sig1, sig2 = key.sign(d['h'], d['k'])
|
||||
self.assertEquals(sig1, d['sig1'])
|
||||
self.assertEquals(sig2, d['sig2'])
|
||||
|
||||
def test_verification(self):
|
||||
for tv in self.tvs:
|
||||
for as_longs in (0,1):
|
||||
d = self.convert_tv(tv, as_longs)
|
||||
key = ElGamal.construct(d['key'])
|
||||
# Positive test
|
||||
res = key.verify( d['h'], (d['sig1'],d['sig2']) )
|
||||
self.failUnless(res)
|
||||
# Negative test
|
||||
res = key.verify( d['h'], (d['sig1']+1,d['sig2']) )
|
||||
self.failIf(res)
|
||||
|
||||
def convert_tv(self, tv, as_longs=0):
|
||||
"""Convert a test vector from textual form (hexadecimal ascii
|
||||
to either integers or byte strings."""
|
||||
key_comps = 'p','g','y','x'
|
||||
tv2 = {}
|
||||
for c in tv.keys():
|
||||
tv2[c] = a2b_hex(tv[c])
|
||||
if as_longs or c in key_comps or c in ('sig1','sig2'):
|
||||
tv2[c] = bytes_to_long(tv2[c])
|
||||
tv2['key']=[]
|
||||
for c in key_comps:
|
||||
tv2['key'] += [tv2[c]]
|
||||
del tv2[c]
|
||||
return tv2
|
||||
|
||||
def _test_random_key(self, bits):
|
||||
elgObj = ElGamal.generate(bits, Random.new().read)
|
||||
self._check_private_key(elgObj)
|
||||
self._exercise_primitive(elgObj)
|
||||
pub = elgObj.publickey()
|
||||
self._check_public_key(pub)
|
||||
self._exercise_public_primitive(elgObj)
|
||||
|
||||
def _check_private_key(self, elgObj):
|
||||
|
||||
# Check capabilities
|
||||
self.failUnless(elgObj.has_private())
|
||||
self.failUnless(elgObj.can_sign())
|
||||
self.failUnless(elgObj.can_encrypt())
|
||||
|
||||
# Sanity check key data
|
||||
self.failUnless(1<elgObj.g<(elgObj.p-1))
|
||||
self.assertEquals(pow(elgObj.g, elgObj.p-1, elgObj.p), 1)
|
||||
self.failUnless(1<elgObj.x<(elgObj.p-1))
|
||||
self.assertEquals(pow(elgObj.g, elgObj.x, elgObj.p), elgObj.y)
|
||||
|
||||
def _check_public_key(self, elgObj):
|
||||
|
||||
# Check capabilities
|
||||
self.failIf(elgObj.has_private())
|
||||
self.failUnless(elgObj.can_sign())
|
||||
self.failUnless(elgObj.can_encrypt())
|
||||
|
||||
# Sanity check key data
|
||||
self.failUnless(1<elgObj.g<(elgObj.p-1))
|
||||
self.assertEquals(pow(elgObj.g, elgObj.p-1, elgObj.p), 1)
|
||||
|
||||
def _exercise_primitive(self, elgObj):
|
||||
# Test encryption/decryption
|
||||
plaintext = b("Test")
|
||||
ciphertext = elgObj.encrypt(plaintext, 123456789L)
|
||||
plaintextP = elgObj.decrypt(ciphertext)
|
||||
self.assertEquals(plaintext, plaintextP)
|
||||
|
||||
# Test signature/verification
|
||||
signature = elgObj.sign(plaintext, 987654321L)
|
||||
elgObj.verify(plaintext, signature)
|
||||
|
||||
def _exercise_public_primitive(self, elgObj):
|
||||
plaintext = b("Test")
|
||||
ciphertext = elgObj.encrypt(plaintext, 123456789L)
|
||||
|
||||
def get_tests(config={}):
|
||||
tests = []
|
||||
tests += list_test_cases(ElGamalTest)
|
||||
return tests
|
||||
|
||||
if __name__ == '__main__':
|
||||
suite = lambda: unittest.TestSuite(get_tests())
|
||||
unittest.main(defaultTest='suite')
|
||||
|
481
modules/Crypto/SelfTest/PublicKey/test_RSA.py
Normal file
481
modules/Crypto/SelfTest/PublicKey/test_RSA.py
Normal file
@ -0,0 +1,481 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# SelfTest/PublicKey/test_RSA.py: Self-test for the RSA primitive
|
||||
#
|
||||
# Written in 2008 by Dwayne C. Litzenberger <dlitz@dlitz.net>
|
||||
#
|
||||
# ===================================================================
|
||||
# The contents of this file are dedicated to the public domain. To
|
||||
# the extent that dedication to the public domain is not available,
|
||||
# everyone is granted a worldwide, perpetual, royalty-free,
|
||||
# non-exclusive license to exercise all rights associated with the
|
||||
# contents of this file for any purpose whatsoever.
|
||||
# No rights are reserved.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
# SOFTWARE.
|
||||
# ===================================================================
|
||||
|
||||
"""Self-test suite for Crypto.PublicKey.RSA"""
|
||||
|
||||
__revision__ = "$Id$"
|
||||
|
||||
import sys
|
||||
import os
|
||||
import pickle
|
||||
if sys.version_info[0] == 2 and sys.version_info[1] == 1:
|
||||
from Crypto.Util.py21compat import *
|
||||
from Crypto.Util.py3compat import *
|
||||
|
||||
import unittest
|
||||
from Crypto.SelfTest.st_common import list_test_cases, a2b_hex, b2a_hex
|
||||
|
||||
class RSATest(unittest.TestCase):
|
||||
# Test vectors from "RSA-OAEP and RSA-PSS test vectors (.zip file)"
|
||||
# ftp://ftp.rsasecurity.com/pub/pkcs/pkcs-1/pkcs-1v2-1-vec.zip
|
||||
# See RSADSI's PKCS#1 page at
|
||||
# http://www.rsa.com/rsalabs/node.asp?id=2125
|
||||
|
||||
# from oaep-int.txt
|
||||
|
||||
# TODO: PyCrypto treats the message as starting *after* the leading "00"
|
||||
# TODO: That behaviour should probably be changed in the future.
|
||||
plaintext = """
|
||||
eb 7a 19 ac e9 e3 00 63 50 e3 29 50 4b 45 e2
|
||||
ca 82 31 0b 26 dc d8 7d 5c 68 f1 ee a8 f5 52 67
|
||||
c3 1b 2e 8b b4 25 1f 84 d7 e0 b2 c0 46 26 f5 af
|
||||
f9 3e dc fb 25 c9 c2 b3 ff 8a e1 0e 83 9a 2d db
|
||||
4c dc fe 4f f4 77 28 b4 a1 b7 c1 36 2b aa d2 9a
|
||||
b4 8d 28 69 d5 02 41 21 43 58 11 59 1b e3 92 f9
|
||||
82 fb 3e 87 d0 95 ae b4 04 48 db 97 2f 3a c1 4f
|
||||
7b c2 75 19 52 81 ce 32 d2 f1 b7 6d 4d 35 3e 2d
|
||||
"""
|
||||
|
||||
ciphertext = """
|
||||
12 53 e0 4d c0 a5 39 7b b4 4a 7a b8 7e 9b f2 a0
|
||||
39 a3 3d 1e 99 6f c8 2a 94 cc d3 00 74 c9 5d f7
|
||||
63 72 20 17 06 9e 52 68 da 5d 1c 0b 4f 87 2c f6
|
||||
53 c1 1d f8 23 14 a6 79 68 df ea e2 8d ef 04 bb
|
||||
6d 84 b1 c3 1d 65 4a 19 70 e5 78 3b d6 eb 96 a0
|
||||
24 c2 ca 2f 4a 90 fe 9f 2e f5 c9 c1 40 e5 bb 48
|
||||
da 95 36 ad 87 00 c8 4f c9 13 0a de a7 4e 55 8d
|
||||
51 a7 4d df 85 d8 b5 0d e9 68 38 d6 06 3e 09 55
|
||||
"""
|
||||
|
||||
modulus = """
|
||||
bb f8 2f 09 06 82 ce 9c 23 38 ac 2b 9d a8 71 f7
|
||||
36 8d 07 ee d4 10 43 a4 40 d6 b6 f0 74 54 f5 1f
|
||||
b8 df ba af 03 5c 02 ab 61 ea 48 ce eb 6f cd 48
|
||||
76 ed 52 0d 60 e1 ec 46 19 71 9d 8a 5b 8b 80 7f
|
||||
af b8 e0 a3 df c7 37 72 3e e6 b4 b7 d9 3a 25 84
|
||||
ee 6a 64 9d 06 09 53 74 88 34 b2 45 45 98 39 4e
|
||||
e0 aa b1 2d 7b 61 a5 1f 52 7a 9a 41 f6 c1 68 7f
|
||||
e2 53 72 98 ca 2a 8f 59 46 f8 e5 fd 09 1d bd cb
|
||||
"""
|
||||
|
||||
e = 0x11L # public exponent
|
||||
|
||||
prime_factor = """
|
||||
c9 7f b1 f0 27 f4 53 f6 34 12 33 ea aa d1 d9 35
|
||||
3f 6c 42 d0 88 66 b1 d0 5a 0f 20 35 02 8b 9d 86
|
||||
98 40 b4 16 66 b4 2e 92 ea 0d a3 b4 32 04 b5 cf
|
||||
ce 33 52 52 4d 04 16 a5 a4 41 e7 00 af 46 15 03
|
||||
"""
|
||||
|
||||
# The same key, in pickled format (from pycrypto 2.3)
|
||||
# to ensure backward compatibility
|
||||
pickled_key_2_3 = \
|
||||
"(iCrypto.PublicKey.RSA\n_RSAobj\np0\n(dp2\nS'e'\np3\nL17L\nsS'd'\np4"\
|
||||
"\nL11646763154293086160147889314553506764606353688284149120983587488"\
|
||||
"79382229568306696406525871631480713149376749558222371890533687587223"\
|
||||
"51580531956820574156366843733156436163097164007967904900300775223658"\
|
||||
"03543233292399245064743971969473468304536714979010219881003396235861"\
|
||||
"8370829441895425705728523874962107052993L\nsS'n'\np5\nL1319966490819"\
|
||||
"88309815009412231606409998872008467220356704480658206329986017741425"\
|
||||
"59273959878490114749026269828326520214759381792655199845793621772998"\
|
||||
"40439054838068985140623386496543388290455526885872858516219460533763"\
|
||||
"92312680578795692682905599590422046720587710762927130740460442438533"\
|
||||
"124053848898103790124491L\nsb."
|
||||
|
||||
def setUp(self):
|
||||
global RSA, Random, bytes_to_long
|
||||
from Crypto.PublicKey import RSA
|
||||
from Crypto import Random
|
||||
from Crypto.Util.number import bytes_to_long, inverse
|
||||
self.n = bytes_to_long(a2b_hex(self.modulus))
|
||||
self.p = bytes_to_long(a2b_hex(self.prime_factor))
|
||||
|
||||
# Compute q, d, and u from n, e, and p
|
||||
self.q = divmod(self.n, self.p)[0]
|
||||
self.d = inverse(self.e, (self.p-1)*(self.q-1))
|
||||
self.u = inverse(self.p, self.q) # u = e**-1 (mod q)
|
||||
|
||||
self.rsa = RSA
|
||||
|
||||
def test_generate_1arg(self):
|
||||
"""RSA (default implementation) generated key (1 argument)"""
|
||||
rsaObj = self.rsa.generate(1024)
|
||||
self._check_private_key(rsaObj)
|
||||
self._exercise_primitive(rsaObj)
|
||||
pub = rsaObj.publickey()
|
||||
self._check_public_key(pub)
|
||||
self._exercise_public_primitive(rsaObj)
|
||||
|
||||
def test_generate_2arg(self):
|
||||
"""RSA (default implementation) generated key (2 arguments)"""
|
||||
rsaObj = self.rsa.generate(1024, Random.new().read)
|
||||
self._check_private_key(rsaObj)
|
||||
self._exercise_primitive(rsaObj)
|
||||
pub = rsaObj.publickey()
|
||||
self._check_public_key(pub)
|
||||
self._exercise_public_primitive(rsaObj)
|
||||
|
||||
def test_generate_3args(self):
|
||||
rsaObj = self.rsa.generate(1024, Random.new().read,e=65537)
|
||||
self._check_private_key(rsaObj)
|
||||
self._exercise_primitive(rsaObj)
|
||||
pub = rsaObj.publickey()
|
||||
self._check_public_key(pub)
|
||||
self._exercise_public_primitive(rsaObj)
|
||||
self.assertEqual(65537,rsaObj.e)
|
||||
|
||||
def test_construct_2tuple(self):
|
||||
"""RSA (default implementation) constructed key (2-tuple)"""
|
||||
pub = self.rsa.construct((self.n, self.e))
|
||||
self._check_public_key(pub)
|
||||
self._check_encryption(pub)
|
||||
self._check_verification(pub)
|
||||
|
||||
def test_construct_3tuple(self):
|
||||
"""RSA (default implementation) constructed key (3-tuple)"""
|
||||
rsaObj = self.rsa.construct((self.n, self.e, self.d))
|
||||
self._check_encryption(rsaObj)
|
||||
self._check_decryption(rsaObj)
|
||||
self._check_signing(rsaObj)
|
||||
self._check_verification(rsaObj)
|
||||
|
||||
def test_construct_4tuple(self):
|
||||
"""RSA (default implementation) constructed key (4-tuple)"""
|
||||
rsaObj = self.rsa.construct((self.n, self.e, self.d, self.p))
|
||||
self._check_encryption(rsaObj)
|
||||
self._check_decryption(rsaObj)
|
||||
self._check_signing(rsaObj)
|
||||
self._check_verification(rsaObj)
|
||||
|
||||
def test_construct_5tuple(self):
|
||||
"""RSA (default implementation) constructed key (5-tuple)"""
|
||||
rsaObj = self.rsa.construct((self.n, self.e, self.d, self.p, self.q))
|
||||
self._check_private_key(rsaObj)
|
||||
self._check_encryption(rsaObj)
|
||||
self._check_decryption(rsaObj)
|
||||
self._check_signing(rsaObj)
|
||||
self._check_verification(rsaObj)
|
||||
|
||||
def test_construct_6tuple(self):
|
||||
"""RSA (default implementation) constructed key (6-tuple)"""
|
||||
rsaObj = self.rsa.construct((self.n, self.e, self.d, self.p, self.q, self.u))
|
||||
self._check_private_key(rsaObj)
|
||||
self._check_encryption(rsaObj)
|
||||
self._check_decryption(rsaObj)
|
||||
self._check_signing(rsaObj)
|
||||
self._check_verification(rsaObj)
|
||||
|
||||
def test_factoring(self):
|
||||
rsaObj = self.rsa.construct([self.n, self.e, self.d])
|
||||
self.failUnless(rsaObj.p==self.p or rsaObj.p==self.q)
|
||||
self.failUnless(rsaObj.q==self.p or rsaObj.q==self.q)
|
||||
self.failUnless(rsaObj.q*rsaObj.p == self.n)
|
||||
|
||||
self.assertRaises(ValueError, self.rsa.construct, [self.n, self.e, self.n-1])
|
||||
|
||||
def test_serialization(self):
|
||||
"""RSA (default implementation) serialize/unserialize key"""
|
||||
rsaObj_orig = self.rsa.generate(1024)
|
||||
rsaObj = pickle.loads(pickle.dumps(rsaObj_orig))
|
||||
self._check_private_key(rsaObj)
|
||||
self._exercise_primitive(rsaObj)
|
||||
pub = rsaObj.publickey()
|
||||
self._check_public_key(pub)
|
||||
self._exercise_public_primitive(rsaObj)
|
||||
|
||||
plaintext = a2b_hex(self.plaintext)
|
||||
ciphertext1 = rsaObj_orig.encrypt(plaintext, b(""))
|
||||
ciphertext2 = rsaObj.encrypt(plaintext, b(""))
|
||||
self.assertEqual(ciphertext1, ciphertext2)
|
||||
|
||||
if not (3, 0) <= sys.version_info < (3, 1, 2, 'final', 0):
|
||||
# Unpickling is broken in Python 3 before 3.1.2 due to http://bugs.python.org/issue6137
|
||||
def test_serialization_compat(self):
|
||||
"""RSA (default implementation) backward compatibility serialization"""
|
||||
rsaObj = pickle.loads(b(self.pickled_key_2_3))
|
||||
plaintext = a2b_hex(self.plaintext)
|
||||
ciphertext = a2b_hex(self.ciphertext)
|
||||
ciphertext_result = rsaObj.encrypt(plaintext, b(""))[0]
|
||||
self.assertEqual(ciphertext_result, ciphertext)
|
||||
|
||||
def _check_private_key(self, rsaObj):
|
||||
# Check capabilities
|
||||
self.assertEqual(1, rsaObj.has_private())
|
||||
self.assertEqual(1, rsaObj.can_sign())
|
||||
self.assertEqual(1, rsaObj.can_encrypt())
|
||||
self.assertEqual(1, rsaObj.can_blind())
|
||||
|
||||
# Check rsaObj.[nedpqu] -> rsaObj.key.[nedpqu] mapping
|
||||
self.assertEqual(rsaObj.n, rsaObj.key.n)
|
||||
self.assertEqual(rsaObj.e, rsaObj.key.e)
|
||||
self.assertEqual(rsaObj.d, rsaObj.key.d)
|
||||
self.assertEqual(rsaObj.p, rsaObj.key.p)
|
||||
self.assertEqual(rsaObj.q, rsaObj.key.q)
|
||||
self.assertEqual(rsaObj.u, rsaObj.key.u)
|
||||
|
||||
# Sanity check key data
|
||||
self.assertEqual(rsaObj.n, rsaObj.p * rsaObj.q) # n = pq
|
||||
self.assertEqual(1, rsaObj.d * rsaObj.e % ((rsaObj.p-1) * (rsaObj.q-1))) # ed = 1 (mod (p-1)(q-1))
|
||||
self.assertEqual(1, rsaObj.p * rsaObj.u % rsaObj.q) # pu = 1 (mod q)
|
||||
self.assertEqual(1, rsaObj.p > 1) # p > 1
|
||||
self.assertEqual(1, rsaObj.q > 1) # q > 1
|
||||
self.assertEqual(1, rsaObj.e > 1) # e > 1
|
||||
self.assertEqual(1, rsaObj.d > 1) # d > 1
|
||||
|
||||
def _check_public_key(self, rsaObj):
|
||||
ciphertext = a2b_hex(self.ciphertext)
|
||||
|
||||
# Check capabilities
|
||||
self.assertEqual(0, rsaObj.has_private())
|
||||
self.assertEqual(1, rsaObj.can_sign())
|
||||
self.assertEqual(1, rsaObj.can_encrypt())
|
||||
self.assertEqual(1, rsaObj.can_blind())
|
||||
|
||||
# Check rsaObj.[ne] -> rsaObj.key.[ne] mapping
|
||||
self.assertEqual(rsaObj.n, rsaObj.key.n)
|
||||
self.assertEqual(rsaObj.e, rsaObj.key.e)
|
||||
|
||||
# Check that private parameters are all missing
|
||||
self.assertEqual(0, hasattr(rsaObj, 'd'))
|
||||
self.assertEqual(0, hasattr(rsaObj, 'p'))
|
||||
self.assertEqual(0, hasattr(rsaObj, 'q'))
|
||||
self.assertEqual(0, hasattr(rsaObj, 'u'))
|
||||
self.assertEqual(0, hasattr(rsaObj.key, 'd'))
|
||||
self.assertEqual(0, hasattr(rsaObj.key, 'p'))
|
||||
self.assertEqual(0, hasattr(rsaObj.key, 'q'))
|
||||
self.assertEqual(0, hasattr(rsaObj.key, 'u'))
|
||||
|
||||
# Sanity check key data
|
||||
self.assertEqual(1, rsaObj.e > 1) # e > 1
|
||||
|
||||
# Public keys should not be able to sign or decrypt
|
||||
self.assertRaises(TypeError, rsaObj.sign, ciphertext, b(""))
|
||||
self.assertRaises(TypeError, rsaObj.decrypt, ciphertext)
|
||||
|
||||
# Check __eq__ and __ne__
|
||||
self.assertEqual(rsaObj.publickey() == rsaObj.publickey(),True) # assert_
|
||||
self.assertEqual(rsaObj.publickey() != rsaObj.publickey(),False) # failIf
|
||||
|
||||
def _exercise_primitive(self, rsaObj):
|
||||
# Since we're using a randomly-generated key, we can't check the test
|
||||
# vector, but we can make sure encryption and decryption are inverse
|
||||
# operations.
|
||||
ciphertext = a2b_hex(self.ciphertext)
|
||||
|
||||
# Test decryption
|
||||
plaintext = rsaObj.decrypt((ciphertext,))
|
||||
|
||||
# Test encryption (2 arguments)
|
||||
(new_ciphertext2,) = rsaObj.encrypt(plaintext, b(""))
|
||||
self.assertEqual(b2a_hex(ciphertext), b2a_hex(new_ciphertext2))
|
||||
|
||||
# Test blinded decryption
|
||||
blinding_factor = Random.new().read(len(ciphertext)-1)
|
||||
blinded_ctext = rsaObj.blind(ciphertext, blinding_factor)
|
||||
blinded_ptext = rsaObj.decrypt((blinded_ctext,))
|
||||
unblinded_plaintext = rsaObj.unblind(blinded_ptext, blinding_factor)
|
||||
self.assertEqual(b2a_hex(plaintext), b2a_hex(unblinded_plaintext))
|
||||
|
||||
# Test signing (2 arguments)
|
||||
signature2 = rsaObj.sign(ciphertext, b(""))
|
||||
self.assertEqual((bytes_to_long(plaintext),), signature2)
|
||||
|
||||
# Test verification
|
||||
self.assertEqual(1, rsaObj.verify(ciphertext, (bytes_to_long(plaintext),)))
|
||||
|
||||
def _exercise_public_primitive(self, rsaObj):
|
||||
plaintext = a2b_hex(self.plaintext)
|
||||
|
||||
# Test encryption (2 arguments)
|
||||
(new_ciphertext2,) = rsaObj.encrypt(plaintext, b(""))
|
||||
|
||||
# Exercise verification
|
||||
rsaObj.verify(new_ciphertext2, (bytes_to_long(plaintext),))
|
||||
|
||||
def _check_encryption(self, rsaObj):
|
||||
plaintext = a2b_hex(self.plaintext)
|
||||
ciphertext = a2b_hex(self.ciphertext)
|
||||
|
||||
# Test encryption (2 arguments)
|
||||
(new_ciphertext2,) = rsaObj.encrypt(plaintext, b(""))
|
||||
self.assertEqual(b2a_hex(ciphertext), b2a_hex(new_ciphertext2))
|
||||
|
||||
def _check_decryption(self, rsaObj):
|
||||
plaintext = a2b_hex(self.plaintext)
|
||||
ciphertext = a2b_hex(self.ciphertext)
|
||||
|
||||
# Test plain decryption
|
||||
new_plaintext = rsaObj.decrypt((ciphertext,))
|
||||
self.assertEqual(b2a_hex(plaintext), b2a_hex(new_plaintext))
|
||||
|
||||
# Test blinded decryption
|
||||
blinding_factor = Random.new().read(len(ciphertext)-1)
|
||||
blinded_ctext = rsaObj.blind(ciphertext, blinding_factor)
|
||||
blinded_ptext = rsaObj.decrypt((blinded_ctext,))
|
||||
unblinded_plaintext = rsaObj.unblind(blinded_ptext, blinding_factor)
|
||||
self.assertEqual(b2a_hex(plaintext), b2a_hex(unblinded_plaintext))
|
||||
|
||||
def _check_verification(self, rsaObj):
|
||||
signature = bytes_to_long(a2b_hex(self.plaintext))
|
||||
message = a2b_hex(self.ciphertext)
|
||||
|
||||
# Test verification
|
||||
t = (signature,) # rsaObj.verify expects a tuple
|
||||
self.assertEqual(1, rsaObj.verify(message, t))
|
||||
|
||||
# Test verification with overlong tuple (this is a
|
||||
# backward-compatibility hack to support some harmless misuse of the
|
||||
# API)
|
||||
t2 = (signature, '')
|
||||
self.assertEqual(1, rsaObj.verify(message, t2)) # extra garbage at end of tuple
|
||||
|
||||
def _check_signing(self, rsaObj):
|
||||
signature = bytes_to_long(a2b_hex(self.plaintext))
|
||||
message = a2b_hex(self.ciphertext)
|
||||
|
||||
# Test signing (2 argument)
|
||||
self.assertEqual((signature,), rsaObj.sign(message, b("")))
|
||||
|
||||
class RSAFastMathTest(RSATest):
|
||||
def setUp(self):
|
||||
RSATest.setUp(self)
|
||||
self.rsa = RSA.RSAImplementation(use_fast_math=True)
|
||||
|
||||
def test_generate_1arg(self):
|
||||
"""RSA (_fastmath implementation) generated key (1 argument)"""
|
||||
RSATest.test_generate_1arg(self)
|
||||
|
||||
def test_generate_2arg(self):
|
||||
"""RSA (_fastmath implementation) generated key (2 arguments)"""
|
||||
RSATest.test_generate_2arg(self)
|
||||
|
||||
def test_construct_2tuple(self):
|
||||
"""RSA (_fastmath implementation) constructed key (2-tuple)"""
|
||||
RSATest.test_construct_2tuple(self)
|
||||
|
||||
def test_construct_3tuple(self):
|
||||
"""RSA (_fastmath implementation) constructed key (3-tuple)"""
|
||||
RSATest.test_construct_3tuple(self)
|
||||
|
||||
def test_construct_4tuple(self):
|
||||
"""RSA (_fastmath implementation) constructed key (4-tuple)"""
|
||||
RSATest.test_construct_4tuple(self)
|
||||
|
||||
def test_construct_5tuple(self):
|
||||
"""RSA (_fastmath implementation) constructed key (5-tuple)"""
|
||||
RSATest.test_construct_5tuple(self)
|
||||
|
||||
def test_construct_6tuple(self):
|
||||
"""RSA (_fastmath implementation) constructed key (6-tuple)"""
|
||||
RSATest.test_construct_6tuple(self)
|
||||
|
||||
def test_factoring(self):
|
||||
RSATest.test_factoring(self)
|
||||
|
||||
|
||||
def test_serialization(self):
|
||||
"""RSA (_fastmath implementation) serialize/unserialize key
|
||||
"""
|
||||
RSATest.test_serialization(self)
|
||||
|
||||
if not (3, 0) <= sys.version_info < (3, 1, 2, 'final', 0):
|
||||
# Unpickling is broken in Python 3 before 3.1.2 due to http://bugs.python.org/issue6137
|
||||
def test_serialization_compat(self):
|
||||
"""RSA (_fastmath implementation) backward compatibility serialization
|
||||
"""
|
||||
RSATest.test_serialization_compat(self)
|
||||
|
||||
|
||||
class RSASlowMathTest(RSATest):
|
||||
def setUp(self):
|
||||
RSATest.setUp(self)
|
||||
self.rsa = RSA.RSAImplementation(use_fast_math=False)
|
||||
|
||||
def test_generate_1arg(self):
|
||||
"""RSA (_slowmath implementation) generated key (1 argument)"""
|
||||
RSATest.test_generate_1arg(self)
|
||||
|
||||
def test_generate_2arg(self):
|
||||
"""RSA (_slowmath implementation) generated key (2 arguments)"""
|
||||
RSATest.test_generate_2arg(self)
|
||||
|
||||
def test_construct_2tuple(self):
|
||||
"""RSA (_slowmath implementation) constructed key (2-tuple)"""
|
||||
RSATest.test_construct_2tuple(self)
|
||||
|
||||
def test_construct_3tuple(self):
|
||||
"""RSA (_slowmath implementation) constructed key (3-tuple)"""
|
||||
RSATest.test_construct_3tuple(self)
|
||||
|
||||
def test_construct_4tuple(self):
|
||||
"""RSA (_slowmath implementation) constructed key (4-tuple)"""
|
||||
RSATest.test_construct_4tuple(self)
|
||||
|
||||
def test_construct_5tuple(self):
|
||||
"""RSA (_slowmath implementation) constructed key (5-tuple)"""
|
||||
RSATest.test_construct_5tuple(self)
|
||||
|
||||
def test_construct_6tuple(self):
|
||||
"""RSA (_slowmath implementation) constructed key (6-tuple)"""
|
||||
RSATest.test_construct_6tuple(self)
|
||||
|
||||
def test_factoring(self):
|
||||
RSATest.test_factoring(self)
|
||||
|
||||
def test_serialization(self):
|
||||
"""RSA (_slowmath implementation) serialize/unserialize key"""
|
||||
RSATest.test_serialization(self)
|
||||
|
||||
if not (3, 0) <= sys.version_info < (3, 1, 2, 'final', 0):
|
||||
# Unpickling is broken in Python 3 before 3.1.2 due to http://bugs.python.org/issue6137
|
||||
def test_serialization_compat(self):
|
||||
"""RSA (_slowmath implementation) backward compatibility serialization
|
||||
"""
|
||||
RSATest.test_serialization_compat(self)
|
||||
|
||||
def get_tests(config={}):
|
||||
tests = []
|
||||
tests += list_test_cases(RSATest)
|
||||
try:
|
||||
from Crypto.PublicKey import _fastmath
|
||||
tests += list_test_cases(RSAFastMathTest)
|
||||
except ImportError:
|
||||
from distutils.sysconfig import get_config_var
|
||||
import inspect
|
||||
_fm_path = os.path.normpath(os.path.dirname(os.path.abspath(
|
||||
inspect.getfile(inspect.currentframe())))
|
||||
+"/../../PublicKey/_fastmath"+get_config_var("SO"))
|
||||
if os.path.exists(_fm_path):
|
||||
raise ImportError("While the _fastmath module exists, importing "+
|
||||
"it failed. This may point to the gmp or mpir shared library "+
|
||||
"not being in the path. _fastmath was found at "+_fm_path)
|
||||
if config.get('slow_tests',1):
|
||||
tests += list_test_cases(RSASlowMathTest)
|
||||
return tests
|
||||
|
||||
if __name__ == '__main__':
|
||||
suite = lambda: unittest.TestSuite(get_tests())
|
||||
unittest.main(defaultTest='suite')
|
||||
|
||||
# vim:set ts=4 sw=4 sts=4 expandtab:
|
389
modules/Crypto/SelfTest/PublicKey/test_import_DSA.py
Normal file
389
modules/Crypto/SelfTest/PublicKey/test_import_DSA.py
Normal file
@ -0,0 +1,389 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# SelfTest/PublicKey/test_import_DSA.py: Self-test for importing DSA keys
|
||||
#
|
||||
# ===================================================================
|
||||
# The contents of this file are dedicated to the public domain. To
|
||||
# the extent that dedication to the public domain is not available,
|
||||
# everyone is granted a worldwide, perpetual, royalty-free,
|
||||
# non-exclusive license to exercise all rights associated with the
|
||||
# contents of this file for any purpose whatsoever.
|
||||
# No rights are reserved.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
# SOFTWARE.
|
||||
# ===================================================================
|
||||
|
||||
import unittest
|
||||
|
||||
from Crypto.PublicKey import DSA, KeyFormatError
|
||||
from Crypto.SelfTest.st_common import *
|
||||
from Crypto.Util.py3compat import *
|
||||
|
||||
from binascii import unhexlify
|
||||
|
||||
class ImportKeyTests(unittest.TestCase):
|
||||
|
||||
y = 92137165128186062214622779787483327510946462589285775188003362705875131352591574106484271700740858696583623951844732128165434284507709057439633739849986759064015013893156866539696757799934634945787496920169462601722830899660681779448742875054459716726855443681559131362852474817534616736104831095601710736729L
|
||||
p = 162452170958135306109773853318304545923250830605675936228618290525164105310663722368377131295055868997377338797580997938253236213714988311430600065853662861806894003694743806769284131194035848116051021923956699231855223389086646903420682639786976554552864568460372266462812137447840653688476258666833303658691L
|
||||
q = 988791743931120302950649732173330531512663554851L
|
||||
g = 85583152299197514738065570254868711517748965097380456700369348466136657764813442044039878840094809620913085570225318356734366886985903212775602770761953571967834823306046501307810937486758039063386311593890777319935391363872375452381836756832784184928202587843258855704771836753434368484556809100537243908232L
|
||||
x = 540873410045082450874416847965843801027716145253L
|
||||
|
||||
def setUp(self):
|
||||
|
||||
# It is easier to write test vectors in text form,
|
||||
# and convert them to byte strigs dynamically here
|
||||
for mname, mvalue in ImportKeyTests.__dict__.items():
|
||||
if mname[:4] in ('der_', 'pem_', 'ssh_'):
|
||||
if mname[:4] == 'der_':
|
||||
mvalue = unhexlify(tobytes(mvalue))
|
||||
mvalue = tobytes(mvalue)
|
||||
setattr(self, mname, mvalue)
|
||||
|
||||
# 1. SubjectPublicKeyInfo
|
||||
der_public=\
|
||||
'308201b73082012b06072a8648ce3804013082011e02818100e756ee1717f4b6'+\
|
||||
'794c7c214724a19763742c45572b4b3f8ff3b44f3be9f44ce039a2757695ec91'+\
|
||||
'5697da74ef914fcd1b05660e2419c761d639f45d2d79b802dbd23e7ab8b81b47'+\
|
||||
'9a380e1f30932584ba2a0b955032342ebc83cb5ca906e7b0d7cd6fe656cecb4c'+\
|
||||
'8b5a77123a8c6750a481e3b06057aff6aa6eba620b832d60c3021500ad32f48c'+\
|
||||
'd3ae0c45a198a61fa4b5e20320763b2302818079dfdc3d614fe635fceb7eaeae'+\
|
||||
'3718dc2efefb45282993ac6749dc83c223d8c1887296316b3b0b54466cf444f3'+\
|
||||
'4b82e3554d0b90a778faaf1306f025dae6a3e36c7f93dd5bac4052b92370040a'+\
|
||||
'ca70b8d5820599711900efbc961812c355dd9beffe0981da85c5548074b41c56'+\
|
||||
'ae43fd300d89262e4efd89943f99a651b03888038185000281810083352a69a1'+\
|
||||
'32f34843d2a0eb995bff4e2f083a73f0049d2c91ea2f0ce43d144abda48199e4'+\
|
||||
'b003c570a8af83303d45105f606c5c48d925a40ed9c2630c2fa4cdbf838539de'+\
|
||||
'b9a29f919085f2046369f627ca84b2cb1e2c7940564b670f963ab1164d4e2ca2'+\
|
||||
'bf6ffd39f12f548928bf4d2d1b5e6980b4f1be4c92a91986fba559'
|
||||
|
||||
def testImportKey1(self):
|
||||
key_obj = self.dsa.importKey(self.der_public)
|
||||
self.failIf(key_obj.has_private())
|
||||
self.assertEqual(self.y, key_obj.key.y)
|
||||
self.assertEqual(self.p, key_obj.key.p)
|
||||
self.assertEqual(self.q, key_obj.key.q)
|
||||
self.assertEqual(self.g, key_obj.key.g)
|
||||
|
||||
def testExportKey1(self):
|
||||
tup = (self.y, self.g, self.p, self.q)
|
||||
key = self.dsa.construct(tup)
|
||||
encoded = key.exportKey('DER')
|
||||
self.assertEqual(self.der_public, encoded)
|
||||
|
||||
# 2.
|
||||
pem_public="""\
|
||||
-----BEGIN DSA PUBLIC KEY-----
|
||||
MIIBtzCCASsGByqGSM44BAEwggEeAoGBAOdW7hcX9LZ5THwhRyShl2N0LEVXK0s/
|
||||
j/O0Tzvp9EzgOaJ1dpXskVaX2nTvkU/NGwVmDiQZx2HWOfRdLXm4AtvSPnq4uBtH
|
||||
mjgOHzCTJYS6KguVUDI0LryDy1ypBuew181v5lbOy0yLWncSOoxnUKSB47BgV6/2
|
||||
qm66YguDLWDDAhUArTL0jNOuDEWhmKYfpLXiAyB2OyMCgYB539w9YU/mNfzrfq6u
|
||||
NxjcLv77RSgpk6xnSdyDwiPYwYhyljFrOwtURmz0RPNLguNVTQuQp3j6rxMG8CXa
|
||||
5qPjbH+T3VusQFK5I3AECspwuNWCBZlxGQDvvJYYEsNV3Zvv/gmB2oXFVIB0tBxW
|
||||
rkP9MA2JJi5O/YmUP5mmUbA4iAOBhQACgYEAgzUqaaEy80hD0qDrmVv/Ti8IOnPw
|
||||
BJ0skeovDOQ9FEq9pIGZ5LADxXCor4MwPUUQX2BsXEjZJaQO2cJjDC+kzb+DhTne
|
||||
uaKfkZCF8gRjafYnyoSyyx4seUBWS2cPljqxFk1OLKK/b/058S9UiSi/TS0bXmmA
|
||||
tPG+TJKpGYb7pVk=
|
||||
-----END DSA PUBLIC KEY-----"""
|
||||
|
||||
def testImportKey2(self):
|
||||
for pem in (self.pem_public, tostr(self.pem_public)):
|
||||
key_obj = self.dsa.importKey(pem)
|
||||
self.failIf(key_obj.has_private())
|
||||
self.assertEqual(self.y, key_obj.key.y)
|
||||
self.assertEqual(self.p, key_obj.key.p)
|
||||
self.assertEqual(self.q, key_obj.key.q)
|
||||
self.assertEqual(self.g, key_obj.key.g)
|
||||
|
||||
def testExportKey2(self):
|
||||
tup = (self.y, self.g, self.p, self.q)
|
||||
key = self.dsa.construct(tup)
|
||||
encoded = key.exportKey('PEM')
|
||||
self.assertEqual(self.pem_public, encoded)
|
||||
|
||||
# 3. OpenSSL/OpenSSH format
|
||||
der_private=\
|
||||
'308201bb02010002818100e756ee1717f4b6794c7c214724a19763742c45572b'+\
|
||||
'4b3f8ff3b44f3be9f44ce039a2757695ec915697da74ef914fcd1b05660e2419'+\
|
||||
'c761d639f45d2d79b802dbd23e7ab8b81b479a380e1f30932584ba2a0b955032'+\
|
||||
'342ebc83cb5ca906e7b0d7cd6fe656cecb4c8b5a77123a8c6750a481e3b06057'+\
|
||||
'aff6aa6eba620b832d60c3021500ad32f48cd3ae0c45a198a61fa4b5e2032076'+\
|
||||
'3b2302818079dfdc3d614fe635fceb7eaeae3718dc2efefb45282993ac6749dc'+\
|
||||
'83c223d8c1887296316b3b0b54466cf444f34b82e3554d0b90a778faaf1306f0'+\
|
||||
'25dae6a3e36c7f93dd5bac4052b92370040aca70b8d5820599711900efbc9618'+\
|
||||
'12c355dd9beffe0981da85c5548074b41c56ae43fd300d89262e4efd89943f99'+\
|
||||
'a651b038880281810083352a69a132f34843d2a0eb995bff4e2f083a73f0049d'+\
|
||||
'2c91ea2f0ce43d144abda48199e4b003c570a8af83303d45105f606c5c48d925'+\
|
||||
'a40ed9c2630c2fa4cdbf838539deb9a29f919085f2046369f627ca84b2cb1e2c'+\
|
||||
'7940564b670f963ab1164d4e2ca2bf6ffd39f12f548928bf4d2d1b5e6980b4f1'+\
|
||||
'be4c92a91986fba55902145ebd9a3f0b82069d98420986b314215025756065'
|
||||
|
||||
def testImportKey3(self):
|
||||
key_obj = self.dsa.importKey(self.der_private)
|
||||
self.failUnless(key_obj.has_private())
|
||||
self.assertEqual(self.y, key_obj.key.y)
|
||||
self.assertEqual(self.p, key_obj.key.p)
|
||||
self.assertEqual(self.q, key_obj.key.q)
|
||||
self.assertEqual(self.g, key_obj.key.g)
|
||||
self.assertEqual(self.x, key_obj.key.x)
|
||||
|
||||
def testExportKey3(self):
|
||||
tup = (self.y, self.g, self.p, self.q, self.x)
|
||||
key = self.dsa.construct(tup)
|
||||
encoded = key.exportKey('DER', pkcs8=False)
|
||||
self.assertEqual(self.der_private, encoded)
|
||||
|
||||
# 4.
|
||||
pem_private="""\
|
||||
-----BEGIN DSA PRIVATE KEY-----
|
||||
MIIBuwIBAAKBgQDnVu4XF/S2eUx8IUckoZdjdCxFVytLP4/ztE876fRM4DmidXaV
|
||||
7JFWl9p075FPzRsFZg4kGcdh1jn0XS15uALb0j56uLgbR5o4Dh8wkyWEuioLlVAy
|
||||
NC68g8tcqQbnsNfNb+ZWzstMi1p3EjqMZ1CkgeOwYFev9qpuumILgy1gwwIVAK0y
|
||||
9IzTrgxFoZimH6S14gMgdjsjAoGAed/cPWFP5jX8636urjcY3C7++0UoKZOsZ0nc
|
||||
g8Ij2MGIcpYxazsLVEZs9ETzS4LjVU0LkKd4+q8TBvAl2uaj42x/k91brEBSuSNw
|
||||
BArKcLjVggWZcRkA77yWGBLDVd2b7/4JgdqFxVSAdLQcVq5D/TANiSYuTv2JlD+Z
|
||||
plGwOIgCgYEAgzUqaaEy80hD0qDrmVv/Ti8IOnPwBJ0skeovDOQ9FEq9pIGZ5LAD
|
||||
xXCor4MwPUUQX2BsXEjZJaQO2cJjDC+kzb+DhTneuaKfkZCF8gRjafYnyoSyyx4s
|
||||
eUBWS2cPljqxFk1OLKK/b/058S9UiSi/TS0bXmmAtPG+TJKpGYb7pVkCFF69mj8L
|
||||
ggadmEIJhrMUIVAldWBl
|
||||
-----END DSA PRIVATE KEY-----"""
|
||||
|
||||
def testImportKey4(self):
|
||||
for pem in (self.pem_private, tostr(self.pem_private)):
|
||||
key_obj = self.dsa.importKey(pem)
|
||||
self.failUnless(key_obj.has_private())
|
||||
self.assertEqual(self.y, key_obj.key.y)
|
||||
self.assertEqual(self.p, key_obj.key.p)
|
||||
self.assertEqual(self.q, key_obj.key.q)
|
||||
self.assertEqual(self.g, key_obj.key.g)
|
||||
self.assertEqual(self.x, key_obj.key.x)
|
||||
|
||||
def testExportKey4(self):
|
||||
tup = (self.y, self.g, self.p, self.q, self.x)
|
||||
key = self.dsa.construct(tup)
|
||||
encoded = key.exportKey('PEM', pkcs8=False)
|
||||
self.assertEqual(self.pem_private, encoded)
|
||||
|
||||
# 5. PKCS8 (unencrypted)
|
||||
der_pkcs8=\
|
||||
'3082014a0201003082012b06072a8648ce3804013082011e02818100e756ee17'+\
|
||||
'17f4b6794c7c214724a19763742c45572b4b3f8ff3b44f3be9f44ce039a27576'+\
|
||||
'95ec915697da74ef914fcd1b05660e2419c761d639f45d2d79b802dbd23e7ab8'+\
|
||||
'b81b479a380e1f30932584ba2a0b955032342ebc83cb5ca906e7b0d7cd6fe656'+\
|
||||
'cecb4c8b5a77123a8c6750a481e3b06057aff6aa6eba620b832d60c3021500ad'+\
|
||||
'32f48cd3ae0c45a198a61fa4b5e20320763b2302818079dfdc3d614fe635fceb'+\
|
||||
'7eaeae3718dc2efefb45282993ac6749dc83c223d8c1887296316b3b0b54466c'+\
|
||||
'f444f34b82e3554d0b90a778faaf1306f025dae6a3e36c7f93dd5bac4052b923'+\
|
||||
'70040aca70b8d5820599711900efbc961812c355dd9beffe0981da85c5548074'+\
|
||||
'b41c56ae43fd300d89262e4efd89943f99a651b03888041602145ebd9a3f0b82'+\
|
||||
'069d98420986b314215025756065'
|
||||
|
||||
def testImportKey5(self):
|
||||
key_obj = self.dsa.importKey(self.der_pkcs8)
|
||||
self.failUnless(key_obj.has_private())
|
||||
self.assertEqual(self.y, key_obj.key.y)
|
||||
self.assertEqual(self.p, key_obj.key.p)
|
||||
self.assertEqual(self.q, key_obj.key.q)
|
||||
self.assertEqual(self.g, key_obj.key.g)
|
||||
self.assertEqual(self.x, key_obj.key.x)
|
||||
|
||||
def testExportKey5(self):
|
||||
tup = (self.y, self.g, self.p, self.q, self.x)
|
||||
key = self.dsa.construct(tup)
|
||||
encoded = key.exportKey('DER')
|
||||
self.assertEqual(self.der_pkcs8, encoded)
|
||||
encoded = key.exportKey('DER', pkcs8=True)
|
||||
self.assertEqual(self.der_pkcs8, encoded)
|
||||
|
||||
# 6.
|
||||
pem_pkcs8="""\
|
||||
-----BEGIN PRIVATE KEY-----
|
||||
MIIBSgIBADCCASsGByqGSM44BAEwggEeAoGBAOdW7hcX9LZ5THwhRyShl2N0LEVX
|
||||
K0s/j/O0Tzvp9EzgOaJ1dpXskVaX2nTvkU/NGwVmDiQZx2HWOfRdLXm4AtvSPnq4
|
||||
uBtHmjgOHzCTJYS6KguVUDI0LryDy1ypBuew181v5lbOy0yLWncSOoxnUKSB47Bg
|
||||
V6/2qm66YguDLWDDAhUArTL0jNOuDEWhmKYfpLXiAyB2OyMCgYB539w9YU/mNfzr
|
||||
fq6uNxjcLv77RSgpk6xnSdyDwiPYwYhyljFrOwtURmz0RPNLguNVTQuQp3j6rxMG
|
||||
8CXa5qPjbH+T3VusQFK5I3AECspwuNWCBZlxGQDvvJYYEsNV3Zvv/gmB2oXFVIB0
|
||||
tBxWrkP9MA2JJi5O/YmUP5mmUbA4iAQWAhRevZo/C4IGnZhCCYazFCFQJXVgZQ==
|
||||
-----END PRIVATE KEY-----"""
|
||||
|
||||
def testImportKey6(self):
|
||||
for pem in (self.pem_pkcs8, tostr(self.pem_pkcs8)):
|
||||
key_obj = self.dsa.importKey(pem)
|
||||
self.failUnless(key_obj.has_private())
|
||||
self.assertEqual(self.y, key_obj.key.y)
|
||||
self.assertEqual(self.p, key_obj.key.p)
|
||||
self.assertEqual(self.q, key_obj.key.q)
|
||||
self.assertEqual(self.g, key_obj.key.g)
|
||||
self.assertEqual(self.x, key_obj.key.x)
|
||||
|
||||
def testExportKey6(self):
|
||||
tup = (self.y, self.g, self.p, self.q, self.x)
|
||||
key = self.dsa.construct(tup)
|
||||
encoded = key.exportKey('PEM')
|
||||
self.assertEqual(self.pem_pkcs8, encoded)
|
||||
encoded = key.exportKey('PEM', pkcs8=True)
|
||||
self.assertEqual(self.pem_pkcs8, encoded)
|
||||
|
||||
# 7. OpenSSH/RFC4253
|
||||
ssh_pub="""ssh-dss AAAAB3NzaC1kc3MAAACBAOdW7hcX9LZ5THwhRyShl2N0LEVXK0s/j/O0Tzvp9EzgOaJ1dpXskVaX2nTvkU/NGwVmDiQZx2HWOfRdLXm4AtvSPnq4uBtHmjgOHzCTJYS6KguVUDI0LryDy1ypBuew181v5lbOy0yLWncSOoxnUKSB47BgV6/2qm66YguDLWDDAAAAFQCtMvSM064MRaGYph+kteIDIHY7IwAAAIB539w9YU/mNfzrfq6uNxjcLv77RSgpk6xnSdyDwiPYwYhyljFrOwtURmz0RPNLguNVTQuQp3j6rxMG8CXa5qPjbH+T3VusQFK5I3AECspwuNWCBZlxGQDvvJYYEsNV3Zvv/gmB2oXFVIB0tBxWrkP9MA2JJi5O/YmUP5mmUbA4iAAAAIEAgzUqaaEy80hD0qDrmVv/Ti8IOnPwBJ0skeovDOQ9FEq9pIGZ5LADxXCor4MwPUUQX2BsXEjZJaQO2cJjDC+kzb+DhTneuaKfkZCF8gRjafYnyoSyyx4seUBWS2cPljqxFk1OLKK/b/058S9UiSi/TS0bXmmAtPG+TJKpGYb7pVk="""
|
||||
|
||||
def testImportKey7(self):
|
||||
for ssh in (self.ssh_pub, tostr(self.ssh_pub)):
|
||||
key_obj = self.dsa.importKey(ssh)
|
||||
self.failIf(key_obj.has_private())
|
||||
self.assertEqual(self.y, key_obj.key.y)
|
||||
self.assertEqual(self.p, key_obj.key.p)
|
||||
self.assertEqual(self.q, key_obj.key.q)
|
||||
self.assertEqual(self.g, key_obj.key.g)
|
||||
|
||||
def testExportKey7(self):
|
||||
tup = (self.y, self.g, self.p, self.q)
|
||||
key = self.dsa.construct(tup)
|
||||
encoded = key.exportKey('OpenSSH')
|
||||
self.assertEqual(self.ssh_pub, encoded)
|
||||
|
||||
# 8. Encrypted OpenSSL/OpenSSH
|
||||
pem_private_encrypted="""\
|
||||
-----BEGIN DSA PRIVATE KEY-----
|
||||
Proc-Type: 4,ENCRYPTED
|
||||
DEK-Info: AES-128-CBC,70B6908939D65E9F2EB999E8729788CE
|
||||
|
||||
4V6GHRDpCrdZ8MBjbyp5AlGUrjvr2Pn2e2zVxy5RBt4FBj9/pa0ae0nnyUPMLSUU
|
||||
kKyOR0topRYTVRLElm4qVrb5uNZ3hRwfbklr+pSrB7O9eHz9V5sfOQxyODS07JxK
|
||||
k1OdOs70/ouMXLF9EWfAZOmWUccZKHNblUwg1p1UrZIz5jXw4dUE/zqhvXh6d+iC
|
||||
ADsICaBCjCrRQJKDp50h3+ndQjkYBKVH+pj8TiQ79U7lAvdp3+iMghQN6YXs9mdI
|
||||
gFpWw/f97oWM4GHZFqHJ+VSMNFjBiFhAvYV587d7Lk4dhD8sCfbxj42PnfRgUItc
|
||||
nnPqHxmhMQozBWzYM4mQuo3XbF2WlsNFbOzFVyGhw1Bx1s91qvXBVWJh2ozrW0s6
|
||||
HYDV7ZkcTml/4kjA/d+mve6LZ8kuuR1qCiZx6rkffhh1gDN/1Xz3HVvIy/dQ+h9s
|
||||
5zp7PwUoWbhqp3WCOr156P6gR8qo7OlT6wMh33FSXK/mxikHK136fV2shwTKQVII
|
||||
rJBvXpj8nACUmi7scKuTWGeUoXa+dwTZVVe+b+L2U1ZM7+h/neTJiXn7u99PFUwu
|
||||
xVJtxaV37m3aXxtCsPnbBg==
|
||||
-----END DSA PRIVATE KEY-----"""
|
||||
|
||||
def testImportKey8(self):
|
||||
for pem in (self.pem_private_encrypted, tostr(self.pem_private_encrypted)):
|
||||
key_obj = self.dsa.importKey(pem, "PWDTEST")
|
||||
self.failUnless(key_obj.has_private())
|
||||
self.assertEqual(self.y, key_obj.key.y)
|
||||
self.assertEqual(self.p, key_obj.key.p)
|
||||
self.assertEqual(self.q, key_obj.key.q)
|
||||
self.assertEqual(self.g, key_obj.key.g)
|
||||
self.assertEqual(self.x, key_obj.key.x)
|
||||
|
||||
def testExportKey8(self):
|
||||
tup = (self.y, self.g, self.p, self.q, self.x)
|
||||
key = self.dsa.construct(tup)
|
||||
encoded = key.exportKey('PEM', pkcs8=False, passphrase="PWDTEST")
|
||||
key = self.dsa.importKey(encoded, "PWDTEST")
|
||||
self.assertEqual(self.y, key.key.y)
|
||||
self.assertEqual(self.p, key.key.p)
|
||||
self.assertEqual(self.q, key.key.q)
|
||||
self.assertEqual(self.g, key.key.g)
|
||||
self.assertEqual(self.x, key.key.x)
|
||||
|
||||
# 9. Encrypted PKCS8
|
||||
# pbeWithMD5AndDES-CBC
|
||||
pem_pkcs8_encrypted="""\
|
||||
-----BEGIN ENCRYPTED PRIVATE KEY-----
|
||||
MIIBcTAbBgkqhkiG9w0BBQMwDgQI0GC3BJ/jSw8CAggABIIBUHc1cXZpExIE9tC7
|
||||
7ryiW+5ihtF2Ekurq3e408GYSAu5smJjN2bvQXmzRFBz8W38K8eMf1sbWroZ4+zn
|
||||
kZSbb9nSm5kAa8lR2+oF2k+WRswMR/PTC3f/D9STO2X0QxdrzKgIHEcSGSHp5jTx
|
||||
aVvbkCDHo9vhBTl6S3ogZ48As/MEro76+9igUwJ1jNhIQZPJ7e20QH5qDpQFFJN4
|
||||
CKl2ENSEuwGiqBszItFy4dqH0g63ZGZV/xt9wSO9Rd7SK/EbA/dklOxBa5Y/VItM
|
||||
gnIhs9XDMoGYyn6F023EicNJm6g/bVQk81BTTma4tm+12TKGdYm+QkeZvCOMZylr
|
||||
Wv67cKwO3cAXt5C3QXMDgYR64XvuaT5h7C0igMp2afSXJlnbHEbFxQVJlv83T4FM
|
||||
eZ4k+NQDbEL8GiHmFxzDWQAuPPZKJWEEEV2p/To+WOh+kSDHQw==
|
||||
-----END ENCRYPTED PRIVATE KEY-----"""
|
||||
|
||||
def testImportKey9(self):
|
||||
for pem in (self.pem_pkcs8_encrypted, tostr(self.pem_pkcs8_encrypted)):
|
||||
key_obj = self.dsa.importKey(pem, "PWDTEST")
|
||||
self.failUnless(key_obj.has_private())
|
||||
self.assertEqual(self.y, key_obj.key.y)
|
||||
self.assertEqual(self.p, key_obj.key.p)
|
||||
self.assertEqual(self.q, key_obj.key.q)
|
||||
self.assertEqual(self.g, key_obj.key.g)
|
||||
self.assertEqual(self.x, key_obj.key.x)
|
||||
|
||||
# 10. Encrypted PKCS8
|
||||
# pkcs5PBES2 /
|
||||
# pkcs5PBKDF2 (rounds=1000, salt=D725BF1B6B8239F4) /
|
||||
# des-EDE3-CBC (iv=27A1C66C42AFEECE)
|
||||
#
|
||||
der_pkcs8_encrypted=\
|
||||
'30820196304006092a864886f70d01050d3033301b06092a864886f70d01050c'+\
|
||||
'300e0408d725bf1b6b8239f4020203e8301406082a864886f70d0307040827a1'+\
|
||||
'c66c42afeece048201505cacfde7bf8edabb3e0d387950dc872662ea7e9b1ed4'+\
|
||||
'400d2e7e6186284b64668d8d0328c33a9d9397e6f03df7cb68268b0a06b4e22f'+\
|
||||
'7d132821449ecf998a8b696dbc6dd2b19e66d7eb2edfeb4153c1771d49702395'+\
|
||||
'4f36072868b5fcccf93413a5ac4b2eb47d4b3f681c6bd67ae363ed776f45ae47'+\
|
||||
'174a00098a7c930a50f820b227ddf50f9742d8e950d02586ff2dac0e3c372248'+\
|
||||
'e5f9b6a7a02f4004f20c87913e0f7b52bccc209b95d478256a890b31d4c9adec'+\
|
||||
'21a4d157a179a93a3dad06f94f3ce486b46dfa7fc15fd852dd7680bbb2f17478'+\
|
||||
'7e71bd8dbaf81eca7518d76c1d26256e95424864ba45ca5d47d7c5a421be02fa'+\
|
||||
'b94ab01e18593f66cf9094eb5c94b9ecf3aa08b854a195cf87612fbe5e96c426'+\
|
||||
'2b0d573e52dc71ba3f5e468c601e816c49b7d32c698b22175e89aaef0c443770'+\
|
||||
'5ef2f88a116d99d8e2869a4fd09a771b84b49e4ccb79aadcb1c9'
|
||||
|
||||
def testImportKey10(self):
|
||||
key_obj = self.dsa.importKey(self.der_pkcs8_encrypted, "PWDTEST")
|
||||
self.failUnless(key_obj.has_private())
|
||||
self.assertEqual(self.y, key_obj.key.y)
|
||||
self.assertEqual(self.p, key_obj.key.p)
|
||||
self.assertEqual(self.q, key_obj.key.q)
|
||||
self.assertEqual(self.g, key_obj.key.g)
|
||||
self.assertEqual(self.x, key_obj.key.x)
|
||||
|
||||
def testExportKey10(self):
|
||||
tup = (self.y, self.g, self.p, self.q, self.x)
|
||||
key = self.dsa.construct(tup)
|
||||
randfunc = BytesIO(unhexlify(b("27A1C66C42AFEECE") + b("D725BF1B6B8239F4"))).read
|
||||
key._randfunc = randfunc
|
||||
encoded = key.exportKey('DER', pkcs8=True, passphrase="PWDTEST")
|
||||
self.assertEqual(self.der_pkcs8_encrypted, encoded)
|
||||
|
||||
# ----
|
||||
|
||||
def testImportError1(self):
|
||||
self.assertRaises(KeyFormatError, self.dsa.importKey, self.der_pkcs8_encrypted, "wrongpwd")
|
||||
|
||||
def testExportError2(self):
|
||||
tup = (self.y, self.g, self.p, self.q, self.x)
|
||||
key = self.dsa.construct(tup)
|
||||
self.assertRaises(ValueError, key.exportKey, 'DER', pkcs8=False, passphrase="PWDTEST")
|
||||
|
||||
class ImportKeyTestsSlow(ImportKeyTests):
|
||||
def setUp(self):
|
||||
ImportKeyTests.setUp(self)
|
||||
self.dsa = DSA.DSAImplementation(use_fast_math=0)
|
||||
|
||||
class ImportKeyTestsFast(ImportKeyTests):
|
||||
def setUp(self):
|
||||
ImportKeyTests.setUp(self)
|
||||
self.dsa = DSA.DSAImplementation(use_fast_math=1)
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
|
||||
def get_tests(config={}):
|
||||
tests = []
|
||||
try:
|
||||
from Crypto.PublicKey import _fastmath
|
||||
tests += list_test_cases(ImportKeyTestsFast)
|
||||
except ImportError:
|
||||
pass
|
||||
tests += list_test_cases(ImportKeyTestsSlow)
|
||||
return tests
|
||||
|
||||
if __name__ == '__main__':
|
||||
suite = lambda: unittest.TestSuite(get_tests())
|
||||
unittest.main(defaultTest='suite')
|
||||
|
404
modules/Crypto/SelfTest/PublicKey/test_import_RSA.py
Normal file
404
modules/Crypto/SelfTest/PublicKey/test_import_RSA.py
Normal file
@ -0,0 +1,404 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# SelfTest/PublicKey/test_importKey.py: Self-test for importing RSA keys
|
||||
#
|
||||
# ===================================================================
|
||||
# The contents of this file are dedicated to the public domain. To
|
||||
# the extent that dedication to the public domain is not available,
|
||||
# everyone is granted a worldwide, perpetual, royalty-free,
|
||||
# non-exclusive license to exercise all rights associated with the
|
||||
# contents of this file for any purpose whatsoever.
|
||||
# No rights are reserved.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
# SOFTWARE.
|
||||
# ===================================================================
|
||||
|
||||
from __future__ import nested_scopes
|
||||
|
||||
__revision__ = "$Id$"
|
||||
|
||||
import unittest
|
||||
|
||||
from Crypto.PublicKey import RSA
|
||||
from Crypto.SelfTest.st_common import *
|
||||
from Crypto.Util.py3compat import *
|
||||
from Crypto.Util.number import inverse
|
||||
from Crypto.Util import asn1
|
||||
|
||||
def der2pem(der, text='PUBLIC'):
|
||||
import binascii
|
||||
chunks = [ binascii.b2a_base64(der[i:i+48]) for i in range(0, len(der), 48) ]
|
||||
pem = b('-----BEGIN %s KEY-----\n' % text)
|
||||
pem += b('').join(chunks)
|
||||
pem += b('-----END %s KEY-----' % text)
|
||||
return pem
|
||||
|
||||
class ImportKeyTests(unittest.TestCase):
|
||||
# 512-bit RSA key generated with openssl
|
||||
rsaKeyPEM = u'''-----BEGIN RSA PRIVATE KEY-----
|
||||
MIIBOwIBAAJBAL8eJ5AKoIsjURpcEoGubZMxLD7+kT+TLr7UkvEtFrRhDDKMtuII
|
||||
q19FrL4pUIMymPMSLBn3hJLe30Dw48GQM4UCAwEAAQJACUSDEp8RTe32ftq8IwG8
|
||||
Wojl5mAd1wFiIOrZ/Uv8b963WJOJiuQcVN29vxU5+My9GPZ7RA3hrDBEAoHUDPrI
|
||||
OQIhAPIPLz4dphiD9imAkivY31Rc5AfHJiQRA7XixTcjEkojAiEAyh/pJHks/Mlr
|
||||
+rdPNEpotBjfV4M4BkgGAA/ipcmaAjcCIQCHvhwwKVBLzzTscT2HeUdEeBMoiXXK
|
||||
JACAr3sJQJGxIQIgarRp+m1WSKV1MciwMaTOnbU7wxFs9DP1pva76lYBzgUCIQC9
|
||||
n0CnZCJ6IZYqSt0H5N7+Q+2Ro64nuwV/OSQfM6sBwQ==
|
||||
-----END RSA PRIVATE KEY-----'''
|
||||
|
||||
# As above, but this is actually an unencrypted PKCS#8 key
|
||||
rsaKeyPEM8 = u'''-----BEGIN PRIVATE KEY-----
|
||||
MIIBVQIBADANBgkqhkiG9w0BAQEFAASCAT8wggE7AgEAAkEAvx4nkAqgiyNRGlwS
|
||||
ga5tkzEsPv6RP5MuvtSS8S0WtGEMMoy24girX0WsvilQgzKY8xIsGfeEkt7fQPDj
|
||||
wZAzhQIDAQABAkAJRIMSnxFN7fZ+2rwjAbxaiOXmYB3XAWIg6tn9S/xv3rdYk4mK
|
||||
5BxU3b2/FTn4zL0Y9ntEDeGsMEQCgdQM+sg5AiEA8g8vPh2mGIP2KYCSK9jfVFzk
|
||||
B8cmJBEDteLFNyMSSiMCIQDKH+kkeSz8yWv6t080Smi0GN9XgzgGSAYAD+KlyZoC
|
||||
NwIhAIe+HDApUEvPNOxxPYd5R0R4EyiJdcokAICvewlAkbEhAiBqtGn6bVZIpXUx
|
||||
yLAxpM6dtTvDEWz0M/Wm9rvqVgHOBQIhAL2fQKdkInohlipK3Qfk3v5D7ZGjrie7
|
||||
BX85JB8zqwHB
|
||||
-----END PRIVATE KEY-----'''
|
||||
|
||||
# The same RSA private key as in rsaKeyPEM, but now encrypted
|
||||
rsaKeyEncryptedPEM=(
|
||||
|
||||
# PEM encryption
|
||||
# With DES and passphrase 'test'
|
||||
('test', u'''-----BEGIN RSA PRIVATE KEY-----
|
||||
Proc-Type: 4,ENCRYPTED
|
||||
DEK-Info: DES-CBC,AF8F9A40BD2FA2FC
|
||||
|
||||
Ckl9ex1kaVEWhYC2QBmfaF+YPiR4NFkRXA7nj3dcnuFEzBnY5XULupqQpQI3qbfA
|
||||
u8GYS7+b3toWWiHZivHbAAUBPDIZG9hKDyB9Sq2VMARGsX1yW1zhNvZLIiVJzUHs
|
||||
C6NxQ1IJWOXzTew/xM2I26kPwHIvadq+/VaT8gLQdjdH0jOiVNaevjWnLgrn1mLP
|
||||
BCNRMdcexozWtAFNNqSzfW58MJL2OdMi21ED184EFytIc1BlB+FZiGZduwKGuaKy
|
||||
9bMbdb/1PSvsSzPsqW7KSSrTw6MgJAFJg6lzIYvR5F4poTVBxwBX3+EyEmShiaNY
|
||||
IRX3TgQI0IjrVuLmvlZKbGWP18FXj7I7k9tSsNOOzllTTdq3ny5vgM3A+ynfAaxp
|
||||
dysKznQ6P+IoqML1WxAID4aGRMWka+uArOJ148Rbj9s=
|
||||
-----END RSA PRIVATE KEY-----'''),
|
||||
|
||||
# PKCS8 encryption
|
||||
('winter', u'''-----BEGIN ENCRYPTED PRIVATE KEY-----
|
||||
MIIBpjBABgkqhkiG9w0BBQ0wMzAbBgkqhkiG9w0BBQwwDgQIeZIsbW3O+JcCAggA
|
||||
MBQGCCqGSIb3DQMHBAgSM2p0D8FilgSCAWBhFyP2tiGKVpGj3mO8qIBzinU60ApR
|
||||
3unvP+N6j7LVgnV2lFGaXbJ6a1PbQXe+2D6DUyBLo8EMXrKKVLqOMGkFMHc0UaV6
|
||||
R6MmrsRDrbOqdpTuVRW+NVd5J9kQQh4xnfU/QrcPPt7vpJvSf4GzG0n666Ki50OV
|
||||
M/feuVlIiyGXY6UWdVDpcOV72cq02eNUs/1JWdh2uEBvA9fCL0c07RnMrdT+CbJQ
|
||||
NjJ7f8ULtp7xvR9O3Al/yJ4Wv3i4VxF1f3MCXzhlUD4I0ONlr0kJWgeQ80q/cWhw
|
||||
ntvgJwnCn2XR1h6LA8Wp+0ghDTsL2NhJpWd78zClGhyU4r3hqu1XDjoXa7YCXCix
|
||||
jCV15+ViDJzlNCwg+W6lRg18sSLkCT7alviIE0U5tHc6UPbbHwT5QqAxAABaP+nZ
|
||||
CGqJGyiwBzrKebjgSm/KRd4C91XqcsysyH2kKPfT51MLAoD4xelOURBP
|
||||
-----END ENCRYPTED PRIVATE KEY-----'''
|
||||
),
|
||||
)
|
||||
|
||||
rsaPublicKeyPEM = u'''-----BEGIN RSA PUBLIC KEY-----
|
||||
MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAL8eJ5AKoIsjURpcEoGubZMxLD7+kT+T
|
||||
Lr7UkvEtFrRhDDKMtuIIq19FrL4pUIMymPMSLBn3hJLe30Dw48GQM4UCAwEAAQ==
|
||||
-----END RSA PUBLIC KEY-----'''
|
||||
|
||||
# Obtained using 'ssh-keygen -i -m PKCS8 -f rsaPublicKeyPEM'
|
||||
rsaPublicKeyOpenSSH = b('''ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAAAQQC/HieQCqCLI1EaXBKBrm2TMSw+/pE/ky6+1JLxLRa0YQwyjLbiCKtfRay+KVCDMpjzEiwZ94SS3t9A8OPBkDOF comment\n''')
|
||||
|
||||
# The private key, in PKCS#1 format encoded with DER
|
||||
rsaKeyDER = a2b_hex(
|
||||
'''3082013b020100024100bf1e27900aa08b23511a5c1281ae6d93312c3efe
|
||||
913f932ebed492f12d16b4610c328cb6e208ab5f45acbe2950833298f312
|
||||
2c19f78492dedf40f0e3c190338502030100010240094483129f114dedf6
|
||||
7edabc2301bc5a88e5e6601dd7016220ead9fd4bfc6fdeb75893898ae41c
|
||||
54ddbdbf1539f8ccbd18f67b440de1ac30440281d40cfac839022100f20f
|
||||
2f3e1da61883f62980922bd8df545ce407c726241103b5e2c53723124a23
|
||||
022100ca1fe924792cfcc96bfab74f344a68b418df578338064806000fe2
|
||||
a5c99a023702210087be1c3029504bcf34ec713d877947447813288975ca
|
||||
240080af7b094091b12102206ab469fa6d5648a57531c8b031a4ce9db53b
|
||||
c3116cf433f5a6f6bbea5601ce05022100bd9f40a764227a21962a4add07
|
||||
e4defe43ed91a3ae27bb057f39241f33ab01c1
|
||||
'''.replace(" ",""))
|
||||
|
||||
# The private key, in unencrypted PKCS#8 format encoded with DER
|
||||
rsaKeyDER8 = a2b_hex(
|
||||
'''30820155020100300d06092a864886f70d01010105000482013f3082013
|
||||
b020100024100bf1e27900aa08b23511a5c1281ae6d93312c3efe913f932
|
||||
ebed492f12d16b4610c328cb6e208ab5f45acbe2950833298f3122c19f78
|
||||
492dedf40f0e3c190338502030100010240094483129f114dedf67edabc2
|
||||
301bc5a88e5e6601dd7016220ead9fd4bfc6fdeb75893898ae41c54ddbdb
|
||||
f1539f8ccbd18f67b440de1ac30440281d40cfac839022100f20f2f3e1da
|
||||
61883f62980922bd8df545ce407c726241103b5e2c53723124a23022100c
|
||||
a1fe924792cfcc96bfab74f344a68b418df578338064806000fe2a5c99a0
|
||||
23702210087be1c3029504bcf34ec713d877947447813288975ca240080a
|
||||
f7b094091b12102206ab469fa6d5648a57531c8b031a4ce9db53bc3116cf
|
||||
433f5a6f6bbea5601ce05022100bd9f40a764227a21962a4add07e4defe4
|
||||
3ed91a3ae27bb057f39241f33ab01c1
|
||||
'''.replace(" ",""))
|
||||
|
||||
rsaPublicKeyDER = a2b_hex(
|
||||
'''305c300d06092a864886f70d0101010500034b003048024100bf1e27900a
|
||||
a08b23511a5c1281ae6d93312c3efe913f932ebed492f12d16b4610c328c
|
||||
b6e208ab5f45acbe2950833298f3122c19f78492dedf40f0e3c190338502
|
||||
03010001
|
||||
'''.replace(" ",""))
|
||||
|
||||
n = long('BF 1E 27 90 0A A0 8B 23 51 1A 5C 12 81 AE 6D 93 31 2C 3E FE 91 3F 93 2E BE D4 92 F1 2D 16 B4 61 0C 32 8C B6 E2 08 AB 5F 45 AC BE 29 50 83 32 98 F3 12 2C 19 F7 84 92 DE DF 40 F0 E3 C1 90 33 85'.replace(" ",""),16)
|
||||
e = 65537L
|
||||
d = long('09 44 83 12 9F 11 4D ED F6 7E DA BC 23 01 BC 5A 88 E5 E6 60 1D D7 01 62 20 EA D9 FD 4B FC 6F DE B7 58 93 89 8A E4 1C 54 DD BD BF 15 39 F8 CC BD 18 F6 7B 44 0D E1 AC 30 44 02 81 D4 0C FA C8 39'.replace(" ",""),16)
|
||||
p = long('00 F2 0F 2F 3E 1D A6 18 83 F6 29 80 92 2B D8 DF 54 5C E4 07 C7 26 24 11 03 B5 E2 C5 37 23 12 4A 23'.replace(" ",""),16)
|
||||
q = long('00 CA 1F E9 24 79 2C FC C9 6B FA B7 4F 34 4A 68 B4 18 DF 57 83 38 06 48 06 00 0F E2 A5 C9 9A 02 37'.replace(" ",""),16)
|
||||
|
||||
# This is q^{-1} mod p). fastmath and slowmath use pInv (p^{-1}
|
||||
# mod q) instead!
|
||||
qInv = long('00 BD 9F 40 A7 64 22 7A 21 96 2A 4A DD 07 E4 DE FE 43 ED 91 A3 AE 27 BB 05 7F 39 24 1F 33 AB 01 C1'.replace(" ",""),16)
|
||||
pInv = inverse(p,q)
|
||||
|
||||
def testImportKey1(self):
|
||||
"""Verify import of RSAPrivateKey DER SEQUENCE"""
|
||||
key = self.rsa.importKey(self.rsaKeyDER)
|
||||
self.failUnless(key.has_private())
|
||||
self.assertEqual(key.n, self.n)
|
||||
self.assertEqual(key.e, self.e)
|
||||
self.assertEqual(key.d, self.d)
|
||||
self.assertEqual(key.p, self.p)
|
||||
self.assertEqual(key.q, self.q)
|
||||
|
||||
def testImportKey2(self):
|
||||
"""Verify import of SubjectPublicKeyInfo DER SEQUENCE"""
|
||||
key = self.rsa.importKey(self.rsaPublicKeyDER)
|
||||
self.failIf(key.has_private())
|
||||
self.assertEqual(key.n, self.n)
|
||||
self.assertEqual(key.e, self.e)
|
||||
|
||||
def testImportKey3unicode(self):
|
||||
"""Verify import of RSAPrivateKey DER SEQUENCE, encoded with PEM as unicode"""
|
||||
key = RSA.importKey(self.rsaKeyPEM)
|
||||
self.assertEqual(key.has_private(),True) # assert_
|
||||
self.assertEqual(key.n, self.n)
|
||||
self.assertEqual(key.e, self.e)
|
||||
self.assertEqual(key.d, self.d)
|
||||
self.assertEqual(key.p, self.p)
|
||||
self.assertEqual(key.q, self.q)
|
||||
|
||||
def testImportKey3bytes(self):
|
||||
"""Verify import of RSAPrivateKey DER SEQUENCE, encoded with PEM as byte string"""
|
||||
key = RSA.importKey(b(self.rsaKeyPEM))
|
||||
self.assertEqual(key.has_private(),True) # assert_
|
||||
self.assertEqual(key.n, self.n)
|
||||
self.assertEqual(key.e, self.e)
|
||||
self.assertEqual(key.d, self.d)
|
||||
self.assertEqual(key.p, self.p)
|
||||
self.assertEqual(key.q, self.q)
|
||||
|
||||
def testImportKey4unicode(self):
|
||||
"""Verify import of RSAPrivateKey DER SEQUENCE, encoded with PEM as unicode"""
|
||||
key = RSA.importKey(self.rsaPublicKeyPEM)
|
||||
self.assertEqual(key.has_private(),False) # failIf
|
||||
self.assertEqual(key.n, self.n)
|
||||
self.assertEqual(key.e, self.e)
|
||||
|
||||
def testImportKey4bytes(self):
|
||||
"""Verify import of SubjectPublicKeyInfo DER SEQUENCE, encoded with PEM as byte string"""
|
||||
key = RSA.importKey(b(self.rsaPublicKeyPEM))
|
||||
self.assertEqual(key.has_private(),False) # failIf
|
||||
self.assertEqual(key.n, self.n)
|
||||
self.assertEqual(key.e, self.e)
|
||||
|
||||
def testImportKey5(self):
|
||||
"""Verifies that the imported key is still a valid RSA pair"""
|
||||
key = RSA.importKey(self.rsaKeyPEM)
|
||||
idem = key.encrypt(key.decrypt(b("Test")),0)
|
||||
self.assertEqual(idem[0],b("Test"))
|
||||
|
||||
def testImportKey6(self):
|
||||
"""Verifies that the imported key is still a valid RSA pair"""
|
||||
key = RSA.importKey(self.rsaKeyDER)
|
||||
idem = key.encrypt(key.decrypt(b("Test")),0)
|
||||
self.assertEqual(idem[0],b("Test"))
|
||||
|
||||
def testImportKey7(self):
|
||||
"""Verify import of OpenSSH public key"""
|
||||
key = self.rsa.importKey(self.rsaPublicKeyOpenSSH)
|
||||
self.assertEqual(key.n, self.n)
|
||||
self.assertEqual(key.e, self.e)
|
||||
|
||||
def testImportKey8(self):
|
||||
"""Verify import of encrypted PrivateKeyInfo DER SEQUENCE"""
|
||||
for t in self.rsaKeyEncryptedPEM:
|
||||
key = self.rsa.importKey(t[1], t[0])
|
||||
self.failUnless(key.has_private())
|
||||
self.assertEqual(key.n, self.n)
|
||||
self.assertEqual(key.e, self.e)
|
||||
self.assertEqual(key.d, self.d)
|
||||
self.assertEqual(key.p, self.p)
|
||||
self.assertEqual(key.q, self.q)
|
||||
|
||||
def testImportKey9(self):
|
||||
"""Verify import of unencrypted PrivateKeyInfo DER SEQUENCE"""
|
||||
key = self.rsa.importKey(self.rsaKeyDER8)
|
||||
self.failUnless(key.has_private())
|
||||
self.assertEqual(key.n, self.n)
|
||||
self.assertEqual(key.e, self.e)
|
||||
self.assertEqual(key.d, self.d)
|
||||
self.assertEqual(key.p, self.p)
|
||||
self.assertEqual(key.q, self.q)
|
||||
|
||||
def testImportKey10(self):
|
||||
"""Verify import of unencrypted PrivateKeyInfo DER SEQUENCE, encoded with PEM"""
|
||||
key = self.rsa.importKey(self.rsaKeyPEM8)
|
||||
self.failUnless(key.has_private())
|
||||
self.assertEqual(key.n, self.n)
|
||||
self.assertEqual(key.e, self.e)
|
||||
self.assertEqual(key.d, self.d)
|
||||
self.assertEqual(key.p, self.p)
|
||||
self.assertEqual(key.q, self.q)
|
||||
|
||||
def testImportKey11(self):
|
||||
"""Verify import of RSAPublicKey DER SEQUENCE"""
|
||||
der = asn1.DerSequence([17, 3]).encode()
|
||||
key = self.rsa.importKey(der)
|
||||
self.assertEqual(key.n, 17)
|
||||
self.assertEqual(key.e, 3)
|
||||
|
||||
def testImportKey12(self):
|
||||
"""Verify import of RSAPublicKey DER SEQUENCE, encoded with PEM"""
|
||||
der = asn1.DerSequence([17, 3]).encode()
|
||||
pem = der2pem(der)
|
||||
key = self.rsa.importKey(pem)
|
||||
self.assertEqual(key.n, 17)
|
||||
self.assertEqual(key.e, 3)
|
||||
|
||||
###
|
||||
def testExportKey1(self):
|
||||
key = self.rsa.construct([self.n, self.e, self.d, self.p, self.q, self.pInv])
|
||||
derKey = key.exportKey("DER")
|
||||
self.assertEqual(derKey, self.rsaKeyDER)
|
||||
|
||||
def testExportKey2(self):
|
||||
key = self.rsa.construct([self.n, self.e])
|
||||
derKey = key.exportKey("DER")
|
||||
self.assertEqual(derKey, self.rsaPublicKeyDER)
|
||||
|
||||
def testExportKey3(self):
|
||||
key = self.rsa.construct([self.n, self.e, self.d, self.p, self.q, self.pInv])
|
||||
pemKey = key.exportKey("PEM")
|
||||
self.assertEqual(pemKey, b(self.rsaKeyPEM))
|
||||
|
||||
def testExportKey4(self):
|
||||
key = self.rsa.construct([self.n, self.e])
|
||||
pemKey = key.exportKey("PEM")
|
||||
self.assertEqual(pemKey, b(self.rsaPublicKeyPEM))
|
||||
|
||||
def testExportKey5(self):
|
||||
key = self.rsa.construct([self.n, self.e])
|
||||
openssh_1 = key.exportKey("OpenSSH").split()
|
||||
openssh_2 = self.rsaPublicKeyOpenSSH.split()
|
||||
self.assertEqual(openssh_1[0], openssh_2[0])
|
||||
self.assertEqual(openssh_1[1], openssh_2[1])
|
||||
|
||||
def testExportKey7(self):
|
||||
key = self.rsa.construct([self.n, self.e, self.d, self.p, self.q, self.pInv])
|
||||
derKey = key.exportKey("DER", pkcs=8)
|
||||
self.assertEqual(derKey, self.rsaKeyDER8)
|
||||
|
||||
def testExportKey8(self):
|
||||
key = self.rsa.construct([self.n, self.e, self.d, self.p, self.q, self.pInv])
|
||||
pemKey = key.exportKey("PEM", pkcs=8)
|
||||
self.assertEqual(pemKey, b(self.rsaKeyPEM8))
|
||||
|
||||
def testExportKey9(self):
|
||||
key = self.rsa.construct([self.n, self.e, self.d, self.p, self.q, self.pInv])
|
||||
self.assertRaises(ValueError, key.exportKey, "invalid-format")
|
||||
|
||||
def testExportKey10(self):
|
||||
# Export and re-import the encrypted key. It must match.
|
||||
# PEM envelope, PKCS#1, old PEM encryption
|
||||
key = self.rsa.construct([self.n, self.e, self.d, self.p, self.q, self.pInv])
|
||||
outkey = key.exportKey('PEM', 'test')
|
||||
self.failUnless(tostr(outkey).find('4,ENCRYPTED')!=-1)
|
||||
self.failUnless(tostr(outkey).find('BEGIN RSA PRIVATE KEY')!=-1)
|
||||
inkey = RSA.importKey(outkey, 'test')
|
||||
self.assertEqual(key.n, inkey.n)
|
||||
self.assertEqual(key.e, inkey.e)
|
||||
self.assertEqual(key.d, inkey.d)
|
||||
|
||||
def testExportKey11(self):
|
||||
# Export and re-import the encrypted key. It must match.
|
||||
# PEM envelope, PKCS#1, old PEM encryption
|
||||
key = self.rsa.construct([self.n, self.e, self.d, self.p, self.q, self.pInv])
|
||||
outkey = key.exportKey('PEM', 'test', pkcs=1)
|
||||
self.failUnless(tostr(outkey).find('4,ENCRYPTED')!=-1)
|
||||
self.failUnless(tostr(outkey).find('BEGIN RSA PRIVATE KEY')!=-1)
|
||||
inkey = RSA.importKey(outkey, 'test')
|
||||
self.assertEqual(key.n, inkey.n)
|
||||
self.assertEqual(key.e, inkey.e)
|
||||
self.assertEqual(key.d, inkey.d)
|
||||
|
||||
def testExportKey12(self):
|
||||
# Export and re-import the encrypted key. It must match.
|
||||
# PEM envelope, PKCS#8, old PEM encryption
|
||||
key = self.rsa.construct([self.n, self.e, self.d, self.p, self.q, self.pInv])
|
||||
outkey = key.exportKey('PEM', 'test', pkcs=8)
|
||||
self.failUnless(tostr(outkey).find('4,ENCRYPTED')!=-1)
|
||||
self.failUnless(tostr(outkey).find('BEGIN PRIVATE KEY')!=-1)
|
||||
inkey = RSA.importKey(outkey, 'test')
|
||||
self.assertEqual(key.n, inkey.n)
|
||||
self.assertEqual(key.e, inkey.e)
|
||||
self.assertEqual(key.d, inkey.d)
|
||||
|
||||
def testExportKey13(self):
|
||||
# Export and re-import the encrypted key. It must match.
|
||||
# PEM envelope, PKCS#8, PKCS#8 encryption
|
||||
key = self.rsa.construct([self.n, self.e, self.d, self.p, self.q, self.pInv])
|
||||
outkey = key.exportKey('PEM', 'test', pkcs=8,
|
||||
protection='PBKDF2WithHMAC-SHA1AndDES-EDE3-CBC')
|
||||
self.failUnless(tostr(outkey).find('4,ENCRYPTED')==-1)
|
||||
self.failUnless(tostr(outkey).find('BEGIN ENCRYPTED PRIVATE KEY')!=-1)
|
||||
inkey = RSA.importKey(outkey, 'test')
|
||||
self.assertEqual(key.n, inkey.n)
|
||||
self.assertEqual(key.e, inkey.e)
|
||||
self.assertEqual(key.d, inkey.d)
|
||||
|
||||
def testExportKey14(self):
|
||||
# Export and re-import the encrypted key. It must match.
|
||||
# DER envelope, PKCS#8, PKCS#8 encryption
|
||||
key = self.rsa.construct([self.n, self.e, self.d, self.p, self.q, self.pInv])
|
||||
outkey = key.exportKey('DER', 'test', pkcs=8)
|
||||
inkey = RSA.importKey(outkey, 'test')
|
||||
self.assertEqual(key.n, inkey.n)
|
||||
self.assertEqual(key.e, inkey.e)
|
||||
self.assertEqual(key.d, inkey.d)
|
||||
|
||||
def testExportKey15(self):
|
||||
# Verify that that error an condition is detected when trying to
|
||||
# use a password with DER encoding and PKCS#1.
|
||||
key = self.rsa.construct([self.n, self.e, self.d, self.p, self.q, self.pInv])
|
||||
self.assertRaises(ValueError, key.exportKey, 'DER', 'test', 1)
|
||||
|
||||
class ImportKeyTestsSlow(ImportKeyTests):
|
||||
def setUp(self):
|
||||
self.rsa = RSA.RSAImplementation(use_fast_math=0)
|
||||
|
||||
class ImportKeyTestsFast(ImportKeyTests):
|
||||
def setUp(self):
|
||||
self.rsa = RSA.RSAImplementation(use_fast_math=1)
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
|
||||
def get_tests(config={}):
|
||||
tests = []
|
||||
try:
|
||||
from Crypto.PublicKey import _fastmath
|
||||
tests += list_test_cases(ImportKeyTestsFast)
|
||||
except ImportError:
|
||||
pass
|
||||
tests += list_test_cases(ImportKeyTestsSlow)
|
||||
return tests
|
||||
|
||||
if __name__ == '__main__':
|
||||
suite = lambda: unittest.TestSuite(get_tests())
|
||||
unittest.main(defaultTest='suite')
|
||||
|
||||
# vim:set ts=4 sw=4 sts=4 expandtab:
|
44
modules/Crypto/SelfTest/Random/Fortuna/__init__.py
Normal file
44
modules/Crypto/SelfTest/Random/Fortuna/__init__.py
Normal file
@ -0,0 +1,44 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# SelfTest/Random/Fortuna/__init__.py: Self-test for Fortuna modules
|
||||
#
|
||||
# Written in 2008 by Dwayne C. Litzenberger <dlitz@dlitz.net>
|
||||
#
|
||||
# ===================================================================
|
||||
# The contents of this file are dedicated to the public domain. To
|
||||
# the extent that dedication to the public domain is not available,
|
||||
# everyone is granted a worldwide, perpetual, royalty-free,
|
||||
# non-exclusive license to exercise all rights associated with the
|
||||
# contents of this file for any purpose whatsoever.
|
||||
# No rights are reserved.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
# SOFTWARE.
|
||||
# ===================================================================
|
||||
|
||||
"""Self-test for the Crypto.Random.Fortuna package"""
|
||||
|
||||
__revision__ = "$Id$"
|
||||
|
||||
import os
|
||||
|
||||
def get_tests(config={}):
|
||||
tests = []
|
||||
from Crypto.SelfTest.Random.Fortuna import test_FortunaAccumulator; tests += test_FortunaAccumulator.get_tests(config=config)
|
||||
from Crypto.SelfTest.Random.Fortuna import test_FortunaGenerator; tests += test_FortunaGenerator.get_tests(config=config)
|
||||
from Crypto.SelfTest.Random.Fortuna import test_SHAd256; tests += test_SHAd256.get_tests(config=config)
|
||||
return tests
|
||||
|
||||
if __name__ == '__main__':
|
||||
import unittest
|
||||
suite = lambda: unittest.TestSuite(get_tests())
|
||||
unittest.main(defaultTest='suite')
|
||||
|
||||
|
||||
# vim:set ts=4 sw=4 sts=4 expandtab:
|
@ -0,0 +1,189 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# SelfTest/Random/Fortuna/test_FortunaAccumulator.py: Self-test for the FortunaAccumulator module
|
||||
#
|
||||
# Written in 2008 by Dwayne C. Litzenberger <dlitz@dlitz.net>
|
||||
#
|
||||
# ===================================================================
|
||||
# The contents of this file are dedicated to the public domain. To
|
||||
# the extent that dedication to the public domain is not available,
|
||||
# everyone is granted a worldwide, perpetual, royalty-free,
|
||||
# non-exclusive license to exercise all rights associated with the
|
||||
# contents of this file for any purpose whatsoever.
|
||||
# No rights are reserved.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
# SOFTWARE.
|
||||
# ===================================================================
|
||||
|
||||
"""Self-tests for Crypto.Random.Fortuna.FortunaAccumulator"""
|
||||
|
||||
__revision__ = "$Id$"
|
||||
|
||||
import sys
|
||||
if sys.version_info[0] == 2 and sys.version_info[1] == 1:
|
||||
from Crypto.Util.py21compat import *
|
||||
from Crypto.Util.py3compat import *
|
||||
|
||||
import unittest
|
||||
from binascii import b2a_hex
|
||||
|
||||
class FortunaAccumulatorTests(unittest.TestCase):
|
||||
def setUp(self):
|
||||
global FortunaAccumulator
|
||||
from Crypto.Random.Fortuna import FortunaAccumulator
|
||||
|
||||
def test_FortunaPool(self):
|
||||
"""FortunaAccumulator.FortunaPool"""
|
||||
pool = FortunaAccumulator.FortunaPool()
|
||||
self.assertEqual(0, pool.length)
|
||||
self.assertEqual("5df6e0e2761359d30a8275058e299fcc0381534545f55cf43e41983f5d4c9456", pool.hexdigest())
|
||||
|
||||
pool.append(b('abc'))
|
||||
|
||||
self.assertEqual(3, pool.length)
|
||||
self.assertEqual("4f8b42c22dd3729b519ba6f68d2da7cc5b2d606d05daed5ad5128cc03e6c6358", pool.hexdigest())
|
||||
|
||||
pool.append(b("dbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq"))
|
||||
|
||||
self.assertEqual(56, pool.length)
|
||||
self.assertEqual(b('0cffe17f68954dac3a84fb1458bd5ec99209449749b2b308b7cb55812f9563af'), b2a_hex(pool.digest()))
|
||||
|
||||
pool.reset()
|
||||
|
||||
self.assertEqual(0, pool.length)
|
||||
|
||||
pool.append(b('a') * 10**6)
|
||||
|
||||
self.assertEqual(10**6, pool.length)
|
||||
self.assertEqual(b('80d1189477563e1b5206b2749f1afe4807e5705e8bd77887a60187a712156688'), b2a_hex(pool.digest()))
|
||||
|
||||
def test_which_pools(self):
|
||||
"""FortunaAccumulator.which_pools"""
|
||||
|
||||
# which_pools(0) should fail
|
||||
self.assertRaises(AssertionError, FortunaAccumulator.which_pools, 0)
|
||||
|
||||
self.assertEqual(FortunaAccumulator.which_pools(1), [0])
|
||||
self.assertEqual(FortunaAccumulator.which_pools(2), [0, 1])
|
||||
self.assertEqual(FortunaAccumulator.which_pools(3), [0])
|
||||
self.assertEqual(FortunaAccumulator.which_pools(4), [0, 1, 2])
|
||||
self.assertEqual(FortunaAccumulator.which_pools(5), [0])
|
||||
self.assertEqual(FortunaAccumulator.which_pools(6), [0, 1])
|
||||
self.assertEqual(FortunaAccumulator.which_pools(7), [0])
|
||||
self.assertEqual(FortunaAccumulator.which_pools(8), [0, 1, 2, 3])
|
||||
for i in range(1, 32):
|
||||
self.assertEqual(FortunaAccumulator.which_pools(2L**i-1), [0])
|
||||
self.assertEqual(FortunaAccumulator.which_pools(2L**i), range(i+1))
|
||||
self.assertEqual(FortunaAccumulator.which_pools(2L**i+1), [0])
|
||||
self.assertEqual(FortunaAccumulator.which_pools(2L**31), range(32))
|
||||
self.assertEqual(FortunaAccumulator.which_pools(2L**32), range(32))
|
||||
self.assertEqual(FortunaAccumulator.which_pools(2L**33), range(32))
|
||||
self.assertEqual(FortunaAccumulator.which_pools(2L**34), range(32))
|
||||
self.assertEqual(FortunaAccumulator.which_pools(2L**35), range(32))
|
||||
self.assertEqual(FortunaAccumulator.which_pools(2L**36), range(32))
|
||||
self.assertEqual(FortunaAccumulator.which_pools(2L**64), range(32))
|
||||
self.assertEqual(FortunaAccumulator.which_pools(2L**128), range(32))
|
||||
|
||||
def test_accumulator(self):
|
||||
"""FortunaAccumulator.FortunaAccumulator"""
|
||||
fa = FortunaAccumulator.FortunaAccumulator()
|
||||
|
||||
# This should fail, because we haven't seeded the PRNG yet
|
||||
self.assertRaises(AssertionError, fa.random_data, 1)
|
||||
|
||||
# Spread some test data across the pools (source number 42)
|
||||
# This would be horribly insecure in a real system.
|
||||
for p in range(32):
|
||||
fa.add_random_event(42, p, b("X") * 32)
|
||||
self.assertEqual(32+2, fa.pools[p].length)
|
||||
|
||||
# This should still fail, because we haven't seeded the PRNG with 64 bytes yet
|
||||
self.assertRaises(AssertionError, fa.random_data, 1)
|
||||
|
||||
# Add more data
|
||||
for p in range(32):
|
||||
fa.add_random_event(42, p, b("X") * 32)
|
||||
self.assertEqual((32+2)*2, fa.pools[p].length)
|
||||
|
||||
# The underlying RandomGenerator should get seeded with Pool 0
|
||||
# s = SHAd256(chr(42) + chr(32) + "X"*32 + chr(42) + chr(32) + "X"*32)
|
||||
# = SHA256(h'edd546f057b389155a31c32e3975e736c1dec030ddebb137014ecbfb32ed8c6f')
|
||||
# = h'aef42a5dcbddab67e8efa118e1b47fde5d697f89beb971b99e6e8e5e89fbf064'
|
||||
# The counter and the key before reseeding is:
|
||||
# C_0 = 0
|
||||
# K_0 = "\x00" * 32
|
||||
# The counter after reseeding is 1, and the new key after reseeding is
|
||||
# C_1 = 1
|
||||
# K_1 = SHAd256(K_0 || s)
|
||||
# = SHA256(h'0eae3e401389fab86640327ac919ecfcb067359d95469e18995ca889abc119a6')
|
||||
# = h'aafe9d0409fbaaafeb0a1f2ef2014a20953349d3c1c6e6e3b962953bea6184dd'
|
||||
# The first block of random data, therefore, is
|
||||
# r_1 = AES-256(K_1, 1)
|
||||
# = AES-256(K_1, h'01000000000000000000000000000000')
|
||||
# = h'b7b86bd9a27d96d7bb4add1b6b10d157'
|
||||
# The second block of random data is
|
||||
# r_2 = AES-256(K_1, 2)
|
||||
# = AES-256(K_1, h'02000000000000000000000000000000')
|
||||
# = h'2350b1c61253db2f8da233be726dc15f'
|
||||
# The third and fourth blocks of random data (which become the new key) are
|
||||
# r_3 = AES-256(K_1, 3)
|
||||
# = AES-256(K_1, h'03000000000000000000000000000000')
|
||||
# = h'f23ad749f33066ff53d307914fbf5b21'
|
||||
# r_4 = AES-256(K_1, 4)
|
||||
# = AES-256(K_1, h'04000000000000000000000000000000')
|
||||
# = h'da9667c7e86ba247655c9490e9d94a7c'
|
||||
# K_2 = r_3 || r_4
|
||||
# = h'f23ad749f33066ff53d307914fbf5b21da9667c7e86ba247655c9490e9d94a7c'
|
||||
# The final counter value is 5.
|
||||
self.assertEqual("aef42a5dcbddab67e8efa118e1b47fde5d697f89beb971b99e6e8e5e89fbf064",
|
||||
fa.pools[0].hexdigest())
|
||||
self.assertEqual(None, fa.generator.key)
|
||||
self.assertEqual(0, fa.generator.counter.next_value())
|
||||
|
||||
result = fa.random_data(32)
|
||||
|
||||
self.assertEqual(b("b7b86bd9a27d96d7bb4add1b6b10d157" "2350b1c61253db2f8da233be726dc15f"), b2a_hex(result))
|
||||
self.assertEqual(b("f23ad749f33066ff53d307914fbf5b21da9667c7e86ba247655c9490e9d94a7c"), b2a_hex(fa.generator.key))
|
||||
self.assertEqual(5, fa.generator.counter.next_value())
|
||||
|
||||
def test_accumulator_pool_length(self):
|
||||
"""FortunaAccumulator.FortunaAccumulator minimum pool length"""
|
||||
fa = FortunaAccumulator.FortunaAccumulator()
|
||||
|
||||
# This test case is hard-coded to assume that FortunaAccumulator.min_pool_size is 64.
|
||||
self.assertEqual(fa.min_pool_size, 64)
|
||||
|
||||
# The PRNG should not allow us to get random data from it yet
|
||||
self.assertRaises(AssertionError, fa.random_data, 1)
|
||||
|
||||
# Add 60 bytes, 4 at a time (2 header + 2 payload) to each of the 32 pools
|
||||
for i in range(15):
|
||||
for p in range(32):
|
||||
# Add the bytes to the pool
|
||||
fa.add_random_event(2, p, b("XX"))
|
||||
|
||||
# The PRNG should not allow us to get random data from it yet
|
||||
self.assertRaises(AssertionError, fa.random_data, 1)
|
||||
|
||||
# Add 4 more bytes to pool 0
|
||||
fa.add_random_event(2, 0, b("XX"))
|
||||
|
||||
# We should now be able to get data from the accumulator
|
||||
fa.random_data(1)
|
||||
|
||||
def get_tests(config={}):
|
||||
from Crypto.SelfTest.st_common import list_test_cases
|
||||
return list_test_cases(FortunaAccumulatorTests)
|
||||
|
||||
if __name__ == '__main__':
|
||||
suite = lambda: unittest.TestSuite(get_tests())
|
||||
unittest.main(defaultTest='suite')
|
||||
|
||||
# vim:set ts=4 sw=4 sts=4 expandtab:
|
@ -0,0 +1,83 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# SelfTest/Random/Fortuna/test_FortunaGenerator.py: Self-test for the FortunaGenerator module
|
||||
#
|
||||
# Written in 2008 by Dwayne C. Litzenberger <dlitz@dlitz.net>
|
||||
#
|
||||
# ===================================================================
|
||||
# The contents of this file are dedicated to the public domain. To
|
||||
# the extent that dedication to the public domain is not available,
|
||||
# everyone is granted a worldwide, perpetual, royalty-free,
|
||||
# non-exclusive license to exercise all rights associated with the
|
||||
# contents of this file for any purpose whatsoever.
|
||||
# No rights are reserved.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
# SOFTWARE.
|
||||
# ===================================================================
|
||||
|
||||
"""Self-tests for Crypto.Random.Fortuna.FortunaGenerator"""
|
||||
|
||||
__revision__ = "$Id$"
|
||||
|
||||
import sys
|
||||
if sys.version_info[0] == 2 and sys.version_info[1] == 1:
|
||||
from Crypto.Util.py21compat import *
|
||||
from Crypto.Util.py3compat import *
|
||||
|
||||
import unittest
|
||||
from binascii import b2a_hex
|
||||
|
||||
class FortunaGeneratorTests(unittest.TestCase):
|
||||
def setUp(self):
|
||||
global FortunaGenerator
|
||||
from Crypto.Random.Fortuna import FortunaGenerator
|
||||
|
||||
def test_generator(self):
|
||||
"""FortunaGenerator.AESGenerator"""
|
||||
fg = FortunaGenerator.AESGenerator()
|
||||
|
||||
# We shouldn't be able to read data until we've seeded the generator
|
||||
self.assertRaises(Exception, fg.pseudo_random_data, 1)
|
||||
self.assertEqual(0, fg.counter.next_value())
|
||||
|
||||
# Seed the generator, which should set the key and increment the counter.
|
||||
fg.reseed(b("Hello"))
|
||||
self.assertEqual(b("0ea6919d4361551364242a4ba890f8f073676e82cf1a52bb880f7e496648b565"), b2a_hex(fg.key))
|
||||
self.assertEqual(1, fg.counter.next_value())
|
||||
|
||||
# Read 2 full blocks from the generator
|
||||
self.assertEqual(b("7cbe2c17684ac223d08969ee8b565616") + # counter=1
|
||||
b("717661c0d2f4758bd6ba140bf3791abd"), # counter=2
|
||||
b2a_hex(fg.pseudo_random_data(32)))
|
||||
|
||||
# Meanwhile, the generator will have re-keyed itself and incremented its counter
|
||||
self.assertEqual(b("33a1bb21987859caf2bbfc5615bef56d") + # counter=3
|
||||
b("e6b71ff9f37112d0c193a135160862b7"), # counter=4
|
||||
b2a_hex(fg.key))
|
||||
self.assertEqual(5, fg.counter.next_value())
|
||||
|
||||
# Read another 2 blocks from the generator
|
||||
self.assertEqual(b("fd6648ba3086e919cee34904ef09a7ff") + # counter=5
|
||||
b("021f77580558b8c3e9248275f23042bf"), # counter=6
|
||||
b2a_hex(fg.pseudo_random_data(32)))
|
||||
|
||||
|
||||
# Try to read more than 2**20 bytes using the internal function. This should fail.
|
||||
self.assertRaises(AssertionError, fg._pseudo_random_data, 2**20+1)
|
||||
|
||||
def get_tests(config={}):
|
||||
from Crypto.SelfTest.st_common import list_test_cases
|
||||
return list_test_cases(FortunaGeneratorTests)
|
||||
|
||||
if __name__ == '__main__':
|
||||
suite = lambda: unittest.TestSuite(get_tests())
|
||||
unittest.main(defaultTest='suite')
|
||||
|
||||
# vim:set ts=4 sw=4 sts=4 expandtab:
|
55
modules/Crypto/SelfTest/Random/Fortuna/test_SHAd256.py
Normal file
55
modules/Crypto/SelfTest/Random/Fortuna/test_SHAd256.py
Normal file
@ -0,0 +1,55 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# SelfTest/Random/Fortuna/test_SHAd256.py: Self-test for the SHAd256 hash function
|
||||
#
|
||||
# Written in 2008 by Dwayne C. Litzenberger <dlitz@dlitz.net>
|
||||
#
|
||||
# ===================================================================
|
||||
# The contents of this file are dedicated to the public domain. To
|
||||
# the extent that dedication to the public domain is not available,
|
||||
# everyone is granted a worldwide, perpetual, royalty-free,
|
||||
# non-exclusive license to exercise all rights associated with the
|
||||
# contents of this file for any purpose whatsoever.
|
||||
# No rights are reserved.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
# SOFTWARE.
|
||||
# ===================================================================
|
||||
|
||||
"""Self-test suite for Crypto.Random.Fortuna.SHAd256"""
|
||||
|
||||
__revision__ = "$Id$"
|
||||
from Crypto.Util.py3compat import *
|
||||
|
||||
# This is a list of (expected_result, input[, description]) tuples.
|
||||
test_data = [
|
||||
# I could not find any test vectors for SHAd256, so I made these vectors by
|
||||
# feeding some sample data into several plain SHA256 implementations
|
||||
# (including OpenSSL, the "sha256sum" tool, and this implementation).
|
||||
# This is a subset of the resulting test vectors. The complete list can be
|
||||
# found at: http://www.dlitz.net/crypto/shad256-test-vectors/
|
||||
('5df6e0e2761359d30a8275058e299fcc0381534545f55cf43e41983f5d4c9456',
|
||||
'', "'' (empty string)"),
|
||||
('4f8b42c22dd3729b519ba6f68d2da7cc5b2d606d05daed5ad5128cc03e6c6358',
|
||||
'abc'),
|
||||
('0cffe17f68954dac3a84fb1458bd5ec99209449749b2b308b7cb55812f9563af',
|
||||
'abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq')
|
||||
]
|
||||
|
||||
def get_tests(config={}):
|
||||
from Crypto.Random.Fortuna import SHAd256
|
||||
from Crypto.SelfTest.Hash.common import make_hash_tests
|
||||
return make_hash_tests(SHAd256, "SHAd256", test_data, 32)
|
||||
|
||||
if __name__ == '__main__':
|
||||
import unittest
|
||||
suite = lambda: unittest.TestSuite(get_tests())
|
||||
unittest.main(defaultTest='suite')
|
||||
|
||||
# vim:set ts=4 sw=4 sts=4 expandtab:
|
49
modules/Crypto/SelfTest/Random/OSRNG/__init__.py
Normal file
49
modules/Crypto/SelfTest/Random/OSRNG/__init__.py
Normal file
@ -0,0 +1,49 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# SelfTest/Random/OSRNG/__init__.py: Self-test for OSRNG modules
|
||||
#
|
||||
# Written in 2008 by Dwayne C. Litzenberger <dlitz@dlitz.net>
|
||||
#
|
||||
# ===================================================================
|
||||
# The contents of this file are dedicated to the public domain. To
|
||||
# the extent that dedication to the public domain is not available,
|
||||
# everyone is granted a worldwide, perpetual, royalty-free,
|
||||
# non-exclusive license to exercise all rights associated with the
|
||||
# contents of this file for any purpose whatsoever.
|
||||
# No rights are reserved.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
# SOFTWARE.
|
||||
# ===================================================================
|
||||
|
||||
"""Self-test for Crypto.Random.OSRNG package"""
|
||||
|
||||
__revision__ = "$Id$"
|
||||
|
||||
import os
|
||||
|
||||
def get_tests(config={}):
|
||||
tests = []
|
||||
if os.name == 'nt':
|
||||
from Crypto.SelfTest.Random.OSRNG import test_nt; tests += test_nt.get_tests(config=config)
|
||||
from Crypto.SelfTest.Random.OSRNG import test_winrandom; tests += test_winrandom.get_tests(config=config)
|
||||
elif os.name == 'posix':
|
||||
from Crypto.SelfTest.Random.OSRNG import test_posix; tests += test_posix.get_tests(config=config)
|
||||
if hasattr(os, 'urandom'):
|
||||
from Crypto.SelfTest.Random.OSRNG import test_fallback; tests += test_fallback.get_tests(config=config)
|
||||
from Crypto.SelfTest.Random.OSRNG import test_generic; tests += test_generic.get_tests(config=config)
|
||||
return tests
|
||||
|
||||
if __name__ == '__main__':
|
||||
import unittest
|
||||
suite = lambda: unittest.TestSuite(get_tests())
|
||||
unittest.main(defaultTest='suite')
|
||||
|
||||
|
||||
# vim:set ts=4 sw=4 sts=4 expandtab:
|
48
modules/Crypto/SelfTest/Random/OSRNG/test_fallback.py
Normal file
48
modules/Crypto/SelfTest/Random/OSRNG/test_fallback.py
Normal file
@ -0,0 +1,48 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# SelfTest/Util/test_fallback.py: Self-test for the OSRNG.fallback.new() function
|
||||
#
|
||||
# Written in 2008 by Dwayne C. Litzenberger <dlitz@dlitz.net>
|
||||
#
|
||||
# ===================================================================
|
||||
# The contents of this file are dedicated to the public domain. To
|
||||
# the extent that dedication to the public domain is not available,
|
||||
# everyone is granted a worldwide, perpetual, royalty-free,
|
||||
# non-exclusive license to exercise all rights associated with the
|
||||
# contents of this file for any purpose whatsoever.
|
||||
# No rights are reserved.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
# SOFTWARE.
|
||||
# ===================================================================
|
||||
|
||||
"""Self-test suite for Crypto.Random.OSRNG.fallback"""
|
||||
|
||||
__revision__ = "$Id$"
|
||||
|
||||
import unittest
|
||||
|
||||
class SimpleTest(unittest.TestCase):
|
||||
def runTest(self):
|
||||
"""Crypto.Random.OSRNG.fallback.new()"""
|
||||
# Import the OSRNG.nt module and try to use it
|
||||
import Crypto.Random.OSRNG.fallback
|
||||
randobj = Crypto.Random.OSRNG.fallback.new()
|
||||
x = randobj.read(16)
|
||||
y = randobj.read(16)
|
||||
self.assertNotEqual(x, y)
|
||||
|
||||
def get_tests(config={}):
|
||||
return [SimpleTest()]
|
||||
|
||||
if __name__ == '__main__':
|
||||
suite = lambda: unittest.TestSuite(get_tests())
|
||||
unittest.main(defaultTest='suite')
|
||||
|
||||
# vim:set ts=4 sw=4 sts=4 expandtab:
|
48
modules/Crypto/SelfTest/Random/OSRNG/test_generic.py
Normal file
48
modules/Crypto/SelfTest/Random/OSRNG/test_generic.py
Normal file
@ -0,0 +1,48 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# SelfTest/Util/test_generic.py: Self-test for the OSRNG.new() function
|
||||
#
|
||||
# Written in 2008 by Dwayne C. Litzenberger <dlitz@dlitz.net>
|
||||
#
|
||||
# ===================================================================
|
||||
# The contents of this file are dedicated to the public domain. To
|
||||
# the extent that dedication to the public domain is not available,
|
||||
# everyone is granted a worldwide, perpetual, royalty-free,
|
||||
# non-exclusive license to exercise all rights associated with the
|
||||
# contents of this file for any purpose whatsoever.
|
||||
# No rights are reserved.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
# SOFTWARE.
|
||||
# ===================================================================
|
||||
|
||||
"""Self-test suite for Crypto.Random.OSRNG"""
|
||||
|
||||
__revision__ = "$Id$"
|
||||
|
||||
import unittest
|
||||
|
||||
class SimpleTest(unittest.TestCase):
|
||||
def runTest(self):
|
||||
"""Crypto.Random.OSRNG.new()"""
|
||||
# Import the OSRNG module and try to use it
|
||||
import Crypto.Random.OSRNG
|
||||
randobj = Crypto.Random.OSRNG.new()
|
||||
x = randobj.read(16)
|
||||
y = randobj.read(16)
|
||||
self.assertNotEqual(x, y)
|
||||
|
||||
def get_tests(config={}):
|
||||
return [SimpleTest()]
|
||||
|
||||
if __name__ == '__main__':
|
||||
suite = lambda: unittest.TestSuite(get_tests())
|
||||
unittest.main(defaultTest='suite')
|
||||
|
||||
# vim:set ts=4 sw=4 sts=4 expandtab:
|
48
modules/Crypto/SelfTest/Random/OSRNG/test_nt.py
Normal file
48
modules/Crypto/SelfTest/Random/OSRNG/test_nt.py
Normal file
@ -0,0 +1,48 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# SelfTest/Util/test_generic.py: Self-test for the OSRNG.nt.new() function
|
||||
#
|
||||
# Written in 2008 by Dwayne C. Litzenberger <dlitz@dlitz.net>
|
||||
#
|
||||
# ===================================================================
|
||||
# The contents of this file are dedicated to the public domain. To
|
||||
# the extent that dedication to the public domain is not available,
|
||||
# everyone is granted a worldwide, perpetual, royalty-free,
|
||||
# non-exclusive license to exercise all rights associated with the
|
||||
# contents of this file for any purpose whatsoever.
|
||||
# No rights are reserved.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
# SOFTWARE.
|
||||
# ===================================================================
|
||||
|
||||
"""Self-test suite for Crypto.Random.OSRNG.nt"""
|
||||
|
||||
__revision__ = "$Id$"
|
||||
|
||||
import unittest
|
||||
|
||||
class SimpleTest(unittest.TestCase):
|
||||
def runTest(self):
|
||||
"""Crypto.Random.OSRNG.nt.new()"""
|
||||
# Import the OSRNG.nt module and try to use it
|
||||
import Crypto.Random.OSRNG.nt
|
||||
randobj = Crypto.Random.OSRNG.nt.new()
|
||||
x = randobj.read(16)
|
||||
y = randobj.read(16)
|
||||
self.assertNotEqual(x, y)
|
||||
|
||||
def get_tests(config={}):
|
||||
return [SimpleTest()]
|
||||
|
||||
if __name__ == '__main__':
|
||||
suite = lambda: unittest.TestSuite(get_tests())
|
||||
unittest.main(defaultTest='suite')
|
||||
|
||||
# vim:set ts=4 sw=4 sts=4 expandtab:
|
48
modules/Crypto/SelfTest/Random/OSRNG/test_posix.py
Normal file
48
modules/Crypto/SelfTest/Random/OSRNG/test_posix.py
Normal file
@ -0,0 +1,48 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# SelfTest/Util/test_posix.py: Self-test for the OSRNG.posix.new() function
|
||||
#
|
||||
# Written in 2008 by Dwayne C. Litzenberger <dlitz@dlitz.net>
|
||||
#
|
||||
# ===================================================================
|
||||
# The contents of this file are dedicated to the public domain. To
|
||||
# the extent that dedication to the public domain is not available,
|
||||
# everyone is granted a worldwide, perpetual, royalty-free,
|
||||
# non-exclusive license to exercise all rights associated with the
|
||||
# contents of this file for any purpose whatsoever.
|
||||
# No rights are reserved.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
# SOFTWARE.
|
||||
# ===================================================================
|
||||
|
||||
"""Self-test suite for Crypto.Random.OSRNG.posix"""
|
||||
|
||||
__revision__ = "$Id$"
|
||||
|
||||
import unittest
|
||||
|
||||
class SimpleTest(unittest.TestCase):
|
||||
def runTest(self):
|
||||
"""Crypto.Random.OSRNG.posix.new()"""
|
||||
# Import the OSRNG.nt module and try to use it
|
||||
import Crypto.Random.OSRNG.posix
|
||||
randobj = Crypto.Random.OSRNG.posix.new()
|
||||
x = randobj.read(16)
|
||||
y = randobj.read(16)
|
||||
self.assertNotEqual(x, y)
|
||||
|
||||
def get_tests(config={}):
|
||||
return [SimpleTest()]
|
||||
|
||||
if __name__ == '__main__':
|
||||
suite = lambda: unittest.TestSuite(get_tests())
|
||||
unittest.main(defaultTest='suite')
|
||||
|
||||
# vim:set ts=4 sw=4 sts=4 expandtab:
|
48
modules/Crypto/SelfTest/Random/OSRNG/test_winrandom.py
Normal file
48
modules/Crypto/SelfTest/Random/OSRNG/test_winrandom.py
Normal file
@ -0,0 +1,48 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# SelfTest/Util/test_winrandom.py: Self-test for the winrandom module
|
||||
#
|
||||
# Written in 2008 by Dwayne C. Litzenberger <dlitz@dlitz.net>
|
||||
#
|
||||
# ===================================================================
|
||||
# The contents of this file are dedicated to the public domain. To
|
||||
# the extent that dedication to the public domain is not available,
|
||||
# everyone is granted a worldwide, perpetual, royalty-free,
|
||||
# non-exclusive license to exercise all rights associated with the
|
||||
# contents of this file for any purpose whatsoever.
|
||||
# No rights are reserved.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
# SOFTWARE.
|
||||
# ===================================================================
|
||||
|
||||
"""Self-test suite for Crypto.Random.OSRNG.winrandom"""
|
||||
|
||||
__revision__ = "$Id$"
|
||||
|
||||
import unittest
|
||||
|
||||
class SimpleTest(unittest.TestCase):
|
||||
def runTest(self):
|
||||
"""Crypto.Random.OSRNG.winrandom"""
|
||||
# Import the winrandom module and try to use it
|
||||
from Crypto.Random.OSRNG import winrandom
|
||||
randobj = winrandom.new()
|
||||
x = randobj.get_bytes(16)
|
||||
y = randobj.get_bytes(16)
|
||||
self.assertNotEqual(x, y)
|
||||
|
||||
def get_tests(config={}):
|
||||
return [SimpleTest()]
|
||||
|
||||
if __name__ == '__main__':
|
||||
suite = lambda: unittest.TestSuite(get_tests())
|
||||
unittest.main(defaultTest='suite')
|
||||
|
||||
# vim:set ts=4 sw=4 sts=4 expandtab:
|
43
modules/Crypto/SelfTest/Random/__init__.py
Normal file
43
modules/Crypto/SelfTest/Random/__init__.py
Normal file
@ -0,0 +1,43 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# SelfTest/Random/__init__.py: Self-test for random number generation modules
|
||||
#
|
||||
# Written in 2008 by Dwayne C. Litzenberger <dlitz@dlitz.net>
|
||||
#
|
||||
# ===================================================================
|
||||
# The contents of this file are dedicated to the public domain. To
|
||||
# the extent that dedication to the public domain is not available,
|
||||
# everyone is granted a worldwide, perpetual, royalty-free,
|
||||
# non-exclusive license to exercise all rights associated with the
|
||||
# contents of this file for any purpose whatsoever.
|
||||
# No rights are reserved.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
# SOFTWARE.
|
||||
# ===================================================================
|
||||
|
||||
"""Self-test for random number generators"""
|
||||
|
||||
__revision__ = "$Id$"
|
||||
|
||||
def get_tests(config={}):
|
||||
tests = []
|
||||
from Crypto.SelfTest.Random import Fortuna; tests += Fortuna.get_tests(config=config)
|
||||
from Crypto.SelfTest.Random import OSRNG; tests += OSRNG.get_tests(config=config)
|
||||
from Crypto.SelfTest.Random import test_random; tests += test_random.get_tests(config=config)
|
||||
from Crypto.SelfTest.Random import test_rpoolcompat; tests += test_rpoolcompat.get_tests(config=config)
|
||||
from Crypto.SelfTest.Random import test__UserFriendlyRNG; tests += test__UserFriendlyRNG.get_tests(config=config)
|
||||
return tests
|
||||
|
||||
if __name__ == '__main__':
|
||||
import unittest
|
||||
suite = lambda: unittest.TestSuite(get_tests())
|
||||
unittest.main(defaultTest='suite')
|
||||
|
||||
# vim:set ts=4 sw=4 sts=4 expandtab:
|
171
modules/Crypto/SelfTest/Random/test__UserFriendlyRNG.py
Normal file
171
modules/Crypto/SelfTest/Random/test__UserFriendlyRNG.py
Normal file
@ -0,0 +1,171 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Self-tests for the user-friendly Crypto.Random interface
|
||||
#
|
||||
# Written in 2013 by Dwayne C. Litzenberger <dlitz@dlitz.net>
|
||||
#
|
||||
# ===================================================================
|
||||
# The contents of this file are dedicated to the public domain. To
|
||||
# the extent that dedication to the public domain is not available,
|
||||
# everyone is granted a worldwide, perpetual, royalty-free,
|
||||
# non-exclusive license to exercise all rights associated with the
|
||||
# contents of this file for any purpose whatsoever.
|
||||
# No rights are reserved.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
# SOFTWARE.
|
||||
# ===================================================================
|
||||
|
||||
"""Self-test suite for generic Crypto.Random stuff """
|
||||
|
||||
from __future__ import nested_scopes
|
||||
|
||||
__revision__ = "$Id$"
|
||||
|
||||
import binascii
|
||||
import pprint
|
||||
import unittest
|
||||
import os
|
||||
import time
|
||||
import sys
|
||||
if sys.version_info[0] == 2 and sys.version_info[1] == 1:
|
||||
from Crypto.Util.py21compat import *
|
||||
from Crypto.Util.py3compat import *
|
||||
|
||||
try:
|
||||
import multiprocessing
|
||||
except ImportError:
|
||||
multiprocessing = None
|
||||
|
||||
import Crypto.Random._UserFriendlyRNG
|
||||
import Crypto.Random.random
|
||||
|
||||
class RNGForkTest(unittest.TestCase):
|
||||
|
||||
def _get_reseed_count(self):
|
||||
"""
|
||||
Get `FortunaAccumulator.reseed_count`, the global count of the
|
||||
number of times that the PRNG has been reseeded.
|
||||
"""
|
||||
rng_singleton = Crypto.Random._UserFriendlyRNG._get_singleton()
|
||||
rng_singleton._lock.acquire()
|
||||
try:
|
||||
return rng_singleton._fa.reseed_count
|
||||
finally:
|
||||
rng_singleton._lock.release()
|
||||
|
||||
def runTest(self):
|
||||
# Regression test for CVE-2013-1445. We had a bug where, under the
|
||||
# right conditions, two processes might see the same random sequence.
|
||||
|
||||
if sys.platform.startswith('win'): # windows can't fork
|
||||
assert not hasattr(os, 'fork') # ... right?
|
||||
return
|
||||
|
||||
# Wait 150 ms so that we don't trigger the rate-limit prematurely.
|
||||
time.sleep(0.15)
|
||||
|
||||
reseed_count_before = self._get_reseed_count()
|
||||
|
||||
# One or both of these calls together should trigger a reseed right here.
|
||||
Crypto.Random._UserFriendlyRNG._get_singleton().reinit()
|
||||
Crypto.Random.get_random_bytes(1)
|
||||
|
||||
reseed_count_after = self._get_reseed_count()
|
||||
self.assertNotEqual(reseed_count_before, reseed_count_after) # sanity check: test should reseed parent before forking
|
||||
|
||||
rfiles = []
|
||||
for i in range(10):
|
||||
rfd, wfd = os.pipe()
|
||||
if os.fork() == 0:
|
||||
# child
|
||||
os.close(rfd)
|
||||
f = os.fdopen(wfd, "wb")
|
||||
|
||||
Crypto.Random.atfork()
|
||||
|
||||
data = Crypto.Random.get_random_bytes(16)
|
||||
|
||||
f.write(data)
|
||||
f.close()
|
||||
os._exit(0)
|
||||
# parent
|
||||
os.close(wfd)
|
||||
rfiles.append(os.fdopen(rfd, "rb"))
|
||||
|
||||
results = []
|
||||
results_dict = {}
|
||||
for f in rfiles:
|
||||
data = binascii.hexlify(f.read())
|
||||
results.append(data)
|
||||
results_dict[data] = 1
|
||||
f.close()
|
||||
|
||||
if len(results) != len(results_dict.keys()):
|
||||
raise AssertionError("RNG output duplicated across fork():\n%s" %
|
||||
(pprint.pformat(results)))
|
||||
|
||||
|
||||
# For RNGMultiprocessingForkTest
|
||||
def _task_main(q):
|
||||
a = Crypto.Random.get_random_bytes(16)
|
||||
time.sleep(0.1) # wait 100 ms
|
||||
b = Crypto.Random.get_random_bytes(16)
|
||||
q.put(binascii.b2a_hex(a))
|
||||
q.put(binascii.b2a_hex(b))
|
||||
q.put(None) # Wait for acknowledgment
|
||||
|
||||
|
||||
class RNGMultiprocessingForkTest(unittest.TestCase):
|
||||
|
||||
def runTest(self):
|
||||
# Another regression test for CVE-2013-1445. This is basically the
|
||||
# same as RNGForkTest, but less compatible with old versions of Python,
|
||||
# and a little easier to read.
|
||||
|
||||
n_procs = 5
|
||||
manager = multiprocessing.Manager()
|
||||
queues = [manager.Queue(1) for i in range(n_procs)]
|
||||
|
||||
# Reseed the pool
|
||||
time.sleep(0.15)
|
||||
Crypto.Random._UserFriendlyRNG._get_singleton().reinit()
|
||||
Crypto.Random.get_random_bytes(1)
|
||||
|
||||
# Start the child processes
|
||||
pool = multiprocessing.Pool(processes=n_procs, initializer=Crypto.Random.atfork)
|
||||
map_result = pool.map_async(_task_main, queues)
|
||||
|
||||
# Get the results, ensuring that no pool processes are reused.
|
||||
aa = [queues[i].get(30) for i in range(n_procs)]
|
||||
bb = [queues[i].get(30) for i in range(n_procs)]
|
||||
res = list(zip(aa, bb))
|
||||
|
||||
# Shut down the pool
|
||||
map_result.get(30)
|
||||
pool.close()
|
||||
pool.join()
|
||||
|
||||
# Check that the results are unique
|
||||
if len(set(aa)) != len(aa) or len(set(res)) != len(res):
|
||||
raise AssertionError("RNG output duplicated across fork():\n%s" %
|
||||
(pprint.pformat(res),))
|
||||
|
||||
|
||||
def get_tests(config={}):
|
||||
tests = []
|
||||
tests += [RNGForkTest()]
|
||||
if multiprocessing is not None:
|
||||
tests += [RNGMultiprocessingForkTest()]
|
||||
return tests
|
||||
|
||||
if __name__ == '__main__':
|
||||
suite = lambda: unittest.TestSuite(get_tests())
|
||||
unittest.main(defaultTest='suite')
|
||||
|
||||
# vim:set ts=4 sw=4 sts=4 expandtab:
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user