basic functionalty

This commit is contained in:
shim_ 2018-04-29 14:11:24 +02:00
commit ee5db19d8e
256 changed files with 48959 additions and 0 deletions

BIN
icon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

191
main.py Normal file
View 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
View File

@ -0,0 +1,7 @@
<metadata>
<name>Seafile</name>
<shortname>seafile</shortname>
<className>SeafileUploader</className>
<icon>icon.png</icon>
<version>0.1</version>
</metadata>

View 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 )

View 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)

View 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)

View 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)

View 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)

View 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

View 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 )

View 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)

View 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)

View 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)

View 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$"

File diff suppressed because it is too large Load Diff

343
modules/Crypto/Hash/CMAC.py Normal file
View 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
View 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)

View 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

View 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

View 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

View 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

View 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
View 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
View 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
View 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)

View 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']

View 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!'

View 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!'

View 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()

View 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$"

View 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:

View 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

View 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:

View 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

View 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

View 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$"

View 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:

View 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)

View 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:

View 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:

View 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:

View 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:

View 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:

View 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:

View 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:

View 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:

View 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:

View 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:

View 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:

View 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:

View 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:

File diff suppressed because it is too large Load Diff

View 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:

View 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:

View 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:

View 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:

View 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:

View 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:

View 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:

View 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:

View 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:

View 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:

View 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:

View 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')

View 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:

View 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:

View 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:

View 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:

View 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:

View 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:

View 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:

View 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:

View 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:

View 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:

View 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')

View 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')

View 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:

View 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()

View 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

View 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()

View 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()

View 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:

View 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:

View 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')

View 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:

View 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')

View 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:

View 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:

View File

@ -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:

View File

@ -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:

View 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:

View 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:

View 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:

View 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:

View 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:

View 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:

View 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:

View 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:

View 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