add api-v2.

dev
Apex Liu 2021-10-11 16:28:44 +08:00
parent 4c5a262715
commit d11476ad5c
144 changed files with 23459 additions and 44 deletions

1
.gitignore vendored
View File

@ -24,6 +24,7 @@ client/tp_assist_macos/build
# for Python
__pycache__
*.pyc
*.pyi
# for JetBrains IDEs.

View File

@ -1,6 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="Encoding">
<file url="file://$PROJECT_DIR$/client/tp_assist_linux/ts_const.h" charset="GBK" />
<file url="file://$PROJECT_DIR$/client/tp_assist_win/msocketx.h" charset="GBK" />
<file url="file://$PROJECT_DIR$/common/libex/include/ex/ex_log.h" charset="GBK" />
<file url="file://$PROJECT_DIR$/common/libex/include/ex/ex_path.h" charset="GBK" />
<file url="file://$PROJECT_DIR$/common/libex/include/ex/ex_thread.h" charset="GBK" />
@ -13,6 +15,15 @@
<file url="file://$PROJECT_DIR$/common/pyshell/src/pys_core.cpp" charset="GBK" />
<file url="file://$PROJECT_DIR$/common/pyshell/src/pys_core.h" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/common/teleport/teleport_const.h" charset="GBK" />
<file url="file://$PROJECT_DIR$/external/macos/release/include/FreeImage.h" charset="ISO-8859-1" />
<file url="file://$PROJECT_DIR$/external/macos/tmp/FreeImage/Source/FreeImage.h" charset="ISO-8859-1" />
<file url="file://$PROJECT_DIR$/external/macos/tmp/FreeImage/Source/FreeImage/BitmapAccess.cpp" charset="ISO-8859-1" />
<file url="file://$PROJECT_DIR$/external/macos/tmp/FreeImage/Source/FreeImage/GetType.cpp" charset="ISO-8859-1" />
<file url="file://$PROJECT_DIR$/external/macos/tmp/FreeImage/Source/FreeImageToolkit/CopyPaste.cpp" charset="ISO-8859-1" />
<file url="file://$PROJECT_DIR$/external/macos/tmp/FreeImage/Source/FreeImageToolkit/Display.cpp" charset="ISO-8859-1" />
<file url="file://$PROJECT_DIR$/external/macos/tmp/FreeImage/Source/FreeImageToolkit/Rescale.cpp" charset="ISO-8859-1" />
<file url="file://$PROJECT_DIR$/external/macos/tmp/FreeImage/Source/FreeImageToolkit/Resize.cpp" charset="ISO-8859-1" />
<file url="file://$PROJECT_DIR$/external/macos/tmp/FreeImage/Source/FreeImageToolkit/Resize.h" charset="ISO-8859-1" />
<file url="file://$PROJECT_DIR$/server/tp_core/common/base_env.h" charset="GBK" />
<file url="file://$PROJECT_DIR$/server/tp_core/common/base_record.cpp" charset="GBK" />
<file url="file://$PROJECT_DIR$/server/tp_core/common/base_record.h" charset="GBK" />
@ -34,6 +45,7 @@
<file url="file://$PROJECT_DIR$/server/tp_core/protocol/rdp/rdp_proxy.cpp" charset="GBK" />
<file url="file://$PROJECT_DIR$/server/tp_core/protocol/rdp/rdp_session.cpp" charset="GBK" />
<file url="file://$PROJECT_DIR$/server/tp_core/protocol/rdp/rdp_session.h" charset="GBK" />
<file url="file://$PROJECT_DIR$/server/tp_core/protocol/rdp_mstsc/rdp_conn.cpp" charset="GBK" />
<file url="file://$PROJECT_DIR$/server/tp_core/protocol/rdp_mstsc/rdp_proxy.cpp" charset="GBK" />
<file url="file://$PROJECT_DIR$/server/tp_core/protocol/ssh/ssh_recorder.h" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/server/tp_core/protocol/ssh/tpssh_proxy.cpp" charset="GBK" />

View File

@ -29,3 +29,6 @@ else ()
MESSAGE(FATAL_ERROR "unsupported platform: ${CMAKE_SYSTEM_NAME}")
endif ()
IF (CMAKE_BUILD_TYPE STREQUAL Debug)
ADD_DEFINITIONS(-D_DEBUG)
ENDIF()

View File

@ -6,6 +6,7 @@ include(CMakeCfg.txt)
if (OS_LINUX)
add_subdirectory(server/tp_web/src)
endif()
#add_subdirectory(client/tp_assist_linux)
add_subdirectory(server/tp_core/core)
add_subdirectory(server/tp_core/protocol/ssh)
@ -16,6 +17,6 @@ if (EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/server/tp_core/protocol/rdp")
add_subdirectory(server/tp_core/protocol/rdp)
endif ()
if (EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/server/tools/tpr2mp4")
add_subdirectory(server/tools/tpr2mp4)
endif ()
#if (EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/server/tools/tpr2mp4")
# add_subdirectory(server/tools/tpr2mp4)
#endif ()

View File

@ -246,6 +246,10 @@ def make_options():
else:
add_split('prepare for server [build once]')
add_option('x64', 'pysrt', 'Make Python-Runtime for python%s-x64' % env.py_ver_str)
add_split('client side')
# add_option('x64', 'ext-client', 'build external libraries for client')
add_option('x64', 'client', 'build client applications [%s]' % ctx.target_path)
# add_option('x64', 'client-installer', 'make client installer')
add_split('server side')
add_option('x64', 'ext-server', 'build external libraries for server')
add_option('x64', 'server', 'build server applications [%s]' % ctx.target_path)

View File

@ -245,10 +245,28 @@ class BuilderLinux(BuilderBase):
super().__init__()
def build_assist(self):
cc.e('not support linux.')
# cc.e('not support linux.')
cc.n('build tp_assist...')
# def build_rdp(self):
# cc.e('not support linux.')
out_path = os.path.join(env.root_path, 'out', 'client', 'linux', 'bin')
out_files = list()
out_files.append(os.path.join(out_path, 'tp_assist'))
for f in out_files:
if os.path.exists(f):
utils.remove(f)
utils.makedirs(out_path)
projects = ['tp_assist']
utils.cmake(os.path.join(env.root_path, 'cmake-build-linux'), ctx.target_path, False, projects=projects)
for f in out_files:
if os.path.exists(f):
utils.ensure_file_exists(f)
def build_player(self):
cc.e("this is no player for Linux platform yet.")
def build_installer(self):
cc.e('not support linux.')

View File

@ -388,7 +388,7 @@ def nsis_build(nsi_file, _define=''):
raise RuntimeError('make installer with nsis failed. [{}]'.format(nsi_file))
def cmake(work_path, target, force_rebuild, cmake_define='', cmake_pre_define=''):
def cmake(work_path, target, force_rebuild, projects=None, cmake_define='', cmake_pre_define=''):
# I use cmake v3.5 which shipped with CLion.
if env.cmake is None:
raise RuntimeError('where is `cmake`?')
@ -413,13 +413,23 @@ def cmake(work_path, target, force_rebuild, cmake_define='', cmake_pre_define=''
if ret != 0:
raise RuntimeError('build with cmake failed, ret={}. [{}]'.format(ret, target))
cmd = 'make'
# cc.o(cmd)
ret, _ = sys_exec(cmd)
if projects is None:
cmd = 'make'
# cc.o(cmd)
ret, _ = sys_exec(cmd)
if ret != 0:
os.chdir(old_p)
raise RuntimeError('build with cmake failed, ret={}. [{}]'.format(ret, target))
else:
for p in projects:
cmd = 'make {}'.format(p)
# cc.o(cmd)
ret, _ = sys_exec(cmd)
if ret != 0:
os.chdir(old_p)
raise RuntimeError('build with cmake {} failed, ret={}. [{}]'.format(ret, p, target))
os.chdir(old_p)
if ret != 0:
raise RuntimeError('build with cmake failed, ret={}. [{}]'.format(ret, target))
def strip(filename):

View File

@ -5,14 +5,10 @@ if [ `id -u` -ne 0 ];then
echo -e "\e[31mPlease run setup as root.\033[0m"
echo ""
exit 1
fi
fi
PATH_ROOT=$(cd "$(dirname "$0")"; pwd)
PATH_TARGET=/usr/local/eom
# PATH_TARGET=/usr/local/teleport
"${PATH_ROOT}/data/bin/tp_web" --py "${PATH_ROOT}/script/main.py"
# echo ""
# echo -e "\e[32mInstallation done.\033[0m"
# echo ""

View File

@ -28,7 +28,7 @@ static ex_u8 g_run_type = RUN_UNKNOWN;
#define EOM_CORE_SERVICE_NAME L"Teleport Core Service"
static bool _run_daemon();
static bool run_daemon_();
#ifdef EX_OS_WIN32
static int service_install()
@ -54,7 +54,7 @@ static int service_uninstall()
}
#endif
static bool _process_cmd_line(int argc, wchar_t** argv)
static bool process_cmd_line_(int argc, wchar_t** argv)
{
if (argc <= 1)
{
@ -112,7 +112,7 @@ static bool _process_cmd_line(int argc, wchar_t** argv)
}
static int _main_loop()
static int main_loop_()
{
if (g_run_type == RUN_CORE)
return ts_main();
@ -120,11 +120,11 @@ static int _main_loop()
return 1;
}
int _app_main(int argc, wchar_t** argv)
int app_main_(int argc, wchar_t** argv)
{
EXLOG_USE_LOGGER(&g_ex_logger);
if (!_process_cmd_line(argc, argv))
if (!process_cmd_line_(argc, argv))
return 1;
#ifdef EX_DEBUG
@ -171,7 +171,7 @@ int _app_main(int argc, wchar_t** argv)
if (!g_is_debug)
{
if (!_run_daemon())
if (!run_daemon_())
{
EXLOGE("[core] can not run in daemon mode.\n");
return 1;
@ -182,7 +182,7 @@ int _app_main(int argc, wchar_t** argv)
#endif
}
return _main_loop();
return main_loop_();
}
@ -334,27 +334,27 @@ VOID WINAPI service_main(DWORD argc, wchar_t** argv)
#else
// not EX_OS_WIN32
#include <fcntl.h>
#include <signal.h>
#include <csignal>
static void _sig_handler(int signum, siginfo_t* info, void* ptr);
static void sig_handler_(int signum, siginfo_t* info, void* ptr);
int main(int argc, char** argv)
{
struct sigaction act;
struct sigaction act{};
memset(&act, 0, sizeof(act));
act.sa_sigaction = _sig_handler;
act.sa_sigaction = sig_handler_;
act.sa_flags = SA_SIGINFO;
sigaction(SIGINT, &act, nullptr);
wchar_t** wargv = ex_make_wargv(argc, argv);
int ret = _app_main(argc, wargv);
int ret = app_main_(argc, wargv);
ex_free_wargv(argc, wargv);
return ret;
}
void _sig_handler(int signum, siginfo_t* info, void* ptr)
void sig_handler_(int signum, siginfo_t* info, void* ptr)
{
if (signum == SIGINT || signum == SIGTERM)
{
@ -363,7 +363,7 @@ void _sig_handler(int signum, siginfo_t* info, void* ptr)
}
}
static bool _run_daemon()
static bool run_daemon_()
{
pid_t pid = fork();
if (pid < 0)

View File

@ -13,7 +13,7 @@
// start 以服务方式运行
// ... 剩余的所有参数均传递给python脚本
//
//
//
// 执行指定的Python脚本
// tp_web --py [-f FuncName] script_file.py ...
// --py 必须为第一个参数,表示本次执行为执行指定脚本
@ -36,7 +36,7 @@ static ex_wstr g_py_main_func;
#define RUN_UNINST_SRV 4
static ex_u8 g_run_type = RUN_UNKNOWN;
#define EOM_WEB_SERVICE_NAME L"Teleport Web Service"
#define TP_WEB_SERVICE_NAME L"Teleport Web Service"
static bool _run_daemon(void);
@ -50,7 +50,7 @@ static int service_install()
ex_wstr exec_file(g_env.m_exec_file);
exec_file += L" start";
if (EXRV_OK == ex_winsrv_install(EOM_WEB_SERVICE_NAME, EOM_WEB_SERVICE_NAME, exec_file))
if (EXRV_OK == ex_winsrv_install(TP_WEB_SERVICE_NAME, TP_WEB_SERVICE_NAME, exec_file))
return 0;
else
return 1;
@ -58,10 +58,10 @@ static int service_install()
static int service_uninstall()
{
if (EXRV_OK != ex_winsrv_stop(EOM_WEB_SERVICE_NAME))
if (EXRV_OK != ex_winsrv_stop(TP_WEB_SERVICE_NAME))
return 1;
if (EXRV_OK != ex_winsrv_uninstall(EOM_WEB_SERVICE_NAME))
if (EXRV_OK != ex_winsrv_uninstall(TP_WEB_SERVICE_NAME))
return 2;
return 0;
@ -350,7 +350,7 @@ int main()
wchar_t** _argv = ::CommandLineToArgvW(szCmdLine, &_argc); //拆分命令行参数字符串;
ret = _app_main(_argc, _argv);
LocalFree(_argv);
_argv = NULL;
@ -360,7 +360,7 @@ int main()
static bool _run_daemon(void)
{
SERVICE_TABLE_ENTRY DispatchTable[2];
DispatchTable[0].lpServiceName = EOM_WEB_SERVICE_NAME;
DispatchTable[0].lpServiceName = TP_WEB_SERVICE_NAME;
DispatchTable[0].lpServiceProc = service_main;
DispatchTable[1].lpServiceName = NULL;
DispatchTable[1].lpServiceProc = NULL;
@ -430,7 +430,7 @@ VOID WINAPI service_main(DWORD argc, wchar_t** argv)
g_ServiceStatus.dwServiceSpecificExitCode = 0;
g_ServiceStatus.dwCheckPoint = 0;
g_ServiceStatus.dwWaitHint = 0;
g_hServiceStatusHandle = RegisterServiceCtrlHandler(EOM_WEB_SERVICE_NAME, service_handler);
g_hServiceStatusHandle = RegisterServiceCtrlHandler(TP_WEB_SERVICE_NAME, service_handler);
if (g_hServiceStatusHandle == 0)
{
EXLOGE_WIN("RegisterServiceCtrlHandler()");

View File

@ -0,0 +1,250 @@
# -*- 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.
# ===================================================================
"""
Module's constants for the modes of operation supported with AES:
:var MODE_ECB: :ref:`Electronic Code Book (ECB) <ecb_mode>`
:var MODE_CBC: :ref:`Cipher-Block Chaining (CBC) <cbc_mode>`
:var MODE_CFB: :ref:`Cipher FeedBack (CFB) <cfb_mode>`
:var MODE_OFB: :ref:`Output FeedBack (OFB) <ofb_mode>`
:var MODE_CTR: :ref:`CounTer Mode (CTR) <ctr_mode>`
:var MODE_OPENPGP: :ref:`OpenPGP Mode <openpgp_mode>`
:var MODE_CCM: :ref:`Counter with CBC-MAC (CCM) Mode <ccm_mode>`
:var MODE_EAX: :ref:`EAX Mode <eax_mode>`
:var MODE_GCM: :ref:`Galois Counter Mode (GCM) <gcm_mode>`
:var MODE_SIV: :ref:`Syntethic Initialization Vector (SIV) <siv_mode>`
:var MODE_OCB: :ref:`Offset Code Book (OCB) <ocb_mode>`
"""
import sys
from Crypto.Cipher import _create_cipher
from Crypto.Util._raw_api import (load_pycryptodome_raw_lib,
VoidPointer, SmartPointer,
c_size_t, c_uint8_ptr)
from Crypto.Util import _cpu_features
from Crypto.Random import get_random_bytes
_cproto = """
int AES_start_operation(const uint8_t key[],
size_t key_len,
void **pResult);
int AES_encrypt(const void *state,
const uint8_t *in,
uint8_t *out,
size_t data_len);
int AES_decrypt(const void *state,
const uint8_t *in,
uint8_t *out,
size_t data_len);
int AES_stop_operation(void *state);
"""
# Load portable AES
_raw_aes_lib = load_pycryptodome_raw_lib("Crypto.Cipher._raw_aes",
_cproto)
# Try to load AES with AES NI instructions
try:
_raw_aesni_lib = None
if _cpu_features.have_aes_ni():
_raw_aesni_lib = load_pycryptodome_raw_lib("Crypto.Cipher._raw_aesni",
_cproto.replace("AES",
"AESNI"))
# _raw_aesni may not have been compiled in
except OSError:
pass
def _create_base_cipher(dict_parameters):
"""This method instantiates and returns a handle to a low-level
base cipher. It will absorb named parameters in the process."""
use_aesni = dict_parameters.pop("use_aesni", True)
try:
key = dict_parameters.pop("key")
except KeyError:
raise TypeError("Missing 'key' parameter")
if len(key) not in key_size:
raise ValueError("Incorrect AES key length (%d bytes)" % len(key))
if use_aesni and _raw_aesni_lib:
start_operation = _raw_aesni_lib.AESNI_start_operation
stop_operation = _raw_aesni_lib.AESNI_stop_operation
else:
start_operation = _raw_aes_lib.AES_start_operation
stop_operation = _raw_aes_lib.AES_stop_operation
cipher = VoidPointer()
result = start_operation(c_uint8_ptr(key),
c_size_t(len(key)),
cipher.address_of())
if result:
raise ValueError("Error %X while instantiating the AES cipher"
% result)
return SmartPointer(cipher.get(), stop_operation)
def _derive_Poly1305_key_pair(key, nonce):
"""Derive a tuple (r, s, nonce) for a Poly1305 MAC.
If nonce is ``None``, a new 16-byte nonce is generated.
"""
if len(key) != 32:
raise ValueError("Poly1305 with AES requires a 32-byte key")
if nonce is None:
nonce = get_random_bytes(16)
elif len(nonce) != 16:
raise ValueError("Poly1305 with AES requires a 16-byte nonce")
s = new(key[:16], MODE_ECB).encrypt(nonce)
return key[16:], s, nonce
def new(key, mode, *args, **kwargs):
"""Create a new AES cipher.
:param key:
The secret key to use in the symmetric cipher.
It must be 16, 24 or 32 bytes long (respectively for *AES-128*,
*AES-192* or *AES-256*).
For ``MODE_SIV`` only, it doubles to 32, 48, or 64 bytes.
:type key: bytes/bytearray/memoryview
:param mode:
The chaining mode to use for encryption or decryption.
If in doubt, use ``MODE_EAX``.
:type mode: One of the supported ``MODE_*`` constants
:Keyword Arguments:
* **iv** (*bytes*, *bytearray*, *memoryview*) --
(Only applicable for ``MODE_CBC``, ``MODE_CFB``, ``MODE_OFB``,
and ``MODE_OPENPGP`` modes).
The initialization vector to use for encryption or decryption.
For ``MODE_CBC``, ``MODE_CFB``, and ``MODE_OFB`` it must be 16 bytes long.
For ``MODE_OPENPGP`` mode only,
it must be 16 bytes long for encryption
and 18 bytes for decryption (in the latter case, it is
actually the *encrypted* IV which was prefixed to the ciphertext).
If not provided, a random byte string is generated (you must then
read its value with the :attr:`iv` attribute).
* **nonce** (*bytes*, *bytearray*, *memoryview*) --
(Only applicable for ``MODE_CCM``, ``MODE_EAX``, ``MODE_GCM``,
``MODE_SIV``, ``MODE_OCB``, and ``MODE_CTR``).
A value that must never be reused for any other encryption done
with this key (except possibly for ``MODE_SIV``, see below).
For ``MODE_EAX``, ``MODE_GCM`` and ``MODE_SIV`` there are no
restrictions on its length (recommended: **16** bytes).
For ``MODE_CCM``, its length must be in the range **[7..13]**.
Bear in mind that with CCM there is a trade-off between nonce
length and maximum message size. Recommendation: **11** bytes.
For ``MODE_OCB``, its length must be in the range **[1..15]**
(recommended: **15**).
For ``MODE_CTR``, its length must be in the range **[0..15]**
(recommended: **8**).
For ``MODE_SIV``, the nonce is optional, if it is not specified,
then no nonce is being used, which renders the encryption
deterministic.
If not provided, for modes other than ``MODE_SIV```, a random
byte string of the recommended length is used (you must then
read its value with the :attr:`nonce` attribute).
* **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 not specified, it will be assumed to be 8.
* **mac_len** : (*integer*) --
(Only ``MODE_EAX``, ``MODE_GCM``, ``MODE_OCB``, ``MODE_CCM``)
Length of the authentication tag, in bytes.
It must be even and in the range **[4..16]**.
The recommended value (and the default, if not specified) is **16**.
* **msg_len** : (*integer*) --
(Only ``MODE_CCM``). Length of the message to (de)cipher.
If not specified, ``encrypt`` must be called with the entire message.
Similarly, ``decrypt`` can only be called once.
* **assoc_len** : (*integer*) --
(Only ``MODE_CCM``). Length of the associated data.
If not specified, all associated data is buffered internally,
which may represent a problem for very large messages.
* **initial_value** : (*integer* or *bytes/bytearray/memoryview*) --
(Only ``MODE_CTR``).
The initial value for the counter. If not present, the cipher will
start counting from 0. The value is incremented by one for each block.
The counter number is encoded in big endian mode.
* **counter** : (*object*) --
Instance of ``Crypto.Util.Counter``, which allows full customization
of the counter block. This parameter is incompatible to both ``nonce``
and ``initial_value``.
* **use_aesni** : (*boolean*) --
Use Intel AES-NI hardware extensions (default: use if available).
:Return: an AES object, of the applicable mode.
"""
kwargs["add_aes_modes"] = True
return _create_cipher(sys.modules[__name__], key, mode, *args, **kwargs)
MODE_ECB = 1
MODE_CBC = 2
MODE_CFB = 3
MODE_OFB = 5
MODE_CTR = 6
MODE_OPENPGP = 7
MODE_CCM = 8
MODE_EAX = 9
MODE_SIV = 10
MODE_GCM = 11
MODE_OCB = 12
# 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,175 @@
# -*- 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.
# ===================================================================
"""
Module's constants for the modes of operation supported with ARC2:
:var MODE_ECB: :ref:`Electronic Code Book (ECB) <ecb_mode>`
:var MODE_CBC: :ref:`Cipher-Block Chaining (CBC) <cbc_mode>`
:var MODE_CFB: :ref:`Cipher FeedBack (CFB) <cfb_mode>`
:var MODE_OFB: :ref:`Output FeedBack (OFB) <ofb_mode>`
:var MODE_CTR: :ref:`CounTer Mode (CTR) <ctr_mode>`
:var MODE_OPENPGP: :ref:`OpenPGP Mode <openpgp_mode>`
:var MODE_EAX: :ref:`EAX Mode <eax_mode>`
"""
import sys
from Crypto.Cipher import _create_cipher
from Crypto.Util.py3compat import byte_string
from Crypto.Util._raw_api import (load_pycryptodome_raw_lib,
VoidPointer, SmartPointer,
c_size_t, c_uint8_ptr)
_raw_arc2_lib = load_pycryptodome_raw_lib(
"Crypto.Cipher._raw_arc2",
"""
int ARC2_start_operation(const uint8_t key[],
size_t key_len,
size_t effective_key_len,
void **pResult);
int ARC2_encrypt(const void *state,
const uint8_t *in,
uint8_t *out,
size_t data_len);
int ARC2_decrypt(const void *state,
const uint8_t *in,
uint8_t *out,
size_t data_len);
int ARC2_stop_operation(void *state);
"""
)
def _create_base_cipher(dict_parameters):
"""This method instantiates and returns a handle to a low-level
base cipher. It will absorb named parameters in the process."""
try:
key = dict_parameters.pop("key")
except KeyError:
raise TypeError("Missing 'key' parameter")
effective_keylen = dict_parameters.pop("effective_keylen", 1024)
if len(key) not in key_size:
raise ValueError("Incorrect ARC2 key length (%d bytes)" % len(key))
if not (40 <= effective_keylen <= 1024):
raise ValueError("'effective_key_len' must be at least 40 and no larger than 1024 "
"(not %d)" % effective_keylen)
start_operation = _raw_arc2_lib.ARC2_start_operation
stop_operation = _raw_arc2_lib.ARC2_stop_operation
cipher = VoidPointer()
result = start_operation(c_uint8_ptr(key),
c_size_t(len(key)),
c_size_t(effective_keylen),
cipher.address_of())
if result:
raise ValueError("Error %X while instantiating the ARC2 cipher"
% result)
return SmartPointer(cipher.get(), stop_operation)
def new(key, mode, *args, **kwargs):
"""Create a new RC2 cipher.
:param key:
The secret key to use in the symmetric cipher.
Its length can vary from 5 to 128 bytes; the actual search space
(and the cipher strength) can be reduced with the ``effective_keylen`` parameter.
:type key: bytes, bytearray, memoryview
:param mode:
The chaining mode to use for encryption or decryption.
:type mode: One of the supported ``MODE_*`` constants
:Keyword Arguments:
* **iv** (*bytes*, *bytearray*, *memoryview*) --
(Only applicable for ``MODE_CBC``, ``MODE_CFB``, ``MODE_OFB``,
and ``MODE_OPENPGP`` modes).
The initialization vector to use for encryption or decryption.
For ``MODE_CBC``, ``MODE_CFB``, and ``MODE_OFB`` it must be 8 bytes long.
For ``MODE_OPENPGP`` mode only,
it must be 8 bytes long for encryption
and 10 bytes for decryption (in the latter case, it is
actually the *encrypted* IV which was prefixed to the ciphertext).
If not provided, a random byte string is generated (you must then
read its value with the :attr:`iv` attribute).
* **nonce** (*bytes*, *bytearray*, *memoryview*) --
(Only applicable for ``MODE_EAX`` and ``MODE_CTR``).
A value that must never be reused for any other encryption done
with this key.
For ``MODE_EAX`` there are no
restrictions on its length (recommended: **16** bytes).
For ``MODE_CTR``, its length must be in the range **[0..7]**.
If not provided for ``MODE_EAX``, a random byte string is generated (you
can read it back via the ``nonce`` attribute).
* **effective_keylen** (*integer*) --
Optional. Maximum strength in bits of the actual key used by the ARC2 algorithm.
If the supplied ``key`` parameter is longer (in bits) of the value specified
here, it will be weakened to match it.
If not specified, no limitation is applied.
* **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 not specified, it will be assumed to be 8.
* **mac_len** : (*integer*) --
(Only ``MODE_EAX``)
Length of the authentication tag, in bytes.
It must be no longer than 8 (default).
* **initial_value** : (*integer*) --
(Only ``MODE_CTR``). The initial value for the counter within
the counter block. By default it is **0**.
:Return: an ARC2 object, of the applicable mode.
"""
return _create_cipher(sys.modules[__name__], key, mode, *args, **kwargs)
MODE_ECB = 1
MODE_CBC = 2
MODE_CFB = 3
MODE_OFB = 5
MODE_CTR = 6
MODE_OPENPGP = 7
MODE_EAX = 9
# Size of a data block (in bytes)
block_size = 8
# Size of a key (in bytes)
key_size = range(5, 128 + 1)

View File

@ -0,0 +1,137 @@
# -*- 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.
# ===================================================================
from Crypto.Util.py3compat import b
from Crypto.Util._raw_api import (load_pycryptodome_raw_lib, VoidPointer,
create_string_buffer, get_raw_buffer,
SmartPointer, c_size_t, c_uint8_ptr)
_raw_arc4_lib = load_pycryptodome_raw_lib("Crypto.Cipher._ARC4", """
int ARC4_stream_encrypt(void *rc4State, const uint8_t in[],
uint8_t out[], size_t len);
int ARC4_stream_init(uint8_t *key, size_t keylen,
void **pRc4State);
int ARC4_stream_destroy(void *rc4State);
""")
class ARC4Cipher:
"""ARC4 cipher object. Do not create it directly. Use
:func:`Crypto.Cipher.ARC4.new` instead.
"""
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.pop('drop', 0)
if len(key) not in key_size:
raise ValueError("Incorrect ARC4 key length (%d bytes)" %
len(key))
self._state = VoidPointer()
result = _raw_arc4_lib.ARC4_stream_init(c_uint8_ptr(key),
c_size_t(len(key)),
self._state.address_of())
if result != 0:
raise ValueError("Error %d while creating the ARC4 cipher"
% result)
self._state = SmartPointer(self._state.get(),
_raw_arc4_lib.ARC4_stream_destroy)
if ndrop > 0:
# This is OK even if the cipher is used for decryption,
# since encrypt and decrypt are actually the same thing
# with ARC4.
self.encrypt(b'\x00' * ndrop)
self.block_size = 1
self.key_size = len(key)
def encrypt(self, plaintext):
"""Encrypt a piece of data.
:param plaintext: The data to encrypt, of any size.
:type plaintext: bytes, bytearray, memoryview
:returns: the encrypted byte string, of equal length as the
plaintext.
"""
ciphertext = create_string_buffer(len(plaintext))
result = _raw_arc4_lib.ARC4_stream_encrypt(self._state.get(),
c_uint8_ptr(plaintext),
ciphertext,
c_size_t(len(plaintext)))
if result:
raise ValueError("Error %d while encrypting with RC4" % result)
return get_raw_buffer(ciphertext)
def decrypt(self, ciphertext):
"""Decrypt a piece of data.
:param ciphertext: The data to decrypt, of any size.
:type ciphertext: bytes, bytearray, memoryview
:returns: the decrypted byte string, of equal length as the
ciphertext.
"""
try:
return self.encrypt(ciphertext)
except ValueError as e:
raise ValueError(str(e).replace("enc", "dec"))
def new(key, *args, **kwargs):
"""Create a new ARC4 cipher.
:param key:
The secret key to use in the symmetric cipher.
Its length must be in the range ``[5..256]``.
The recommended length is 16 bytes.
:type key: bytes, bytearray, memoryview
:Keyword Arguments:
* *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 = range(5, 256+1)

View File

@ -0,0 +1,159 @@
# -*- 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.
# ===================================================================
"""
Module's constants for the modes of operation supported with Blowfish:
:var MODE_ECB: :ref:`Electronic Code Book (ECB) <ecb_mode>`
:var MODE_CBC: :ref:`Cipher-Block Chaining (CBC) <cbc_mode>`
:var MODE_CFB: :ref:`Cipher FeedBack (CFB) <cfb_mode>`
:var MODE_OFB: :ref:`Output FeedBack (OFB) <ofb_mode>`
:var MODE_CTR: :ref:`CounTer Mode (CTR) <ctr_mode>`
:var MODE_OPENPGP: :ref:`OpenPGP Mode <openpgp_mode>`
:var MODE_EAX: :ref:`EAX Mode <eax_mode>`
"""
import sys
from Crypto.Cipher import _create_cipher
from Crypto.Util._raw_api import (load_pycryptodome_raw_lib,
VoidPointer, SmartPointer, c_size_t,
c_uint8_ptr)
_raw_blowfish_lib = load_pycryptodome_raw_lib(
"Crypto.Cipher._raw_blowfish",
"""
int Blowfish_start_operation(const uint8_t key[],
size_t key_len,
void **pResult);
int Blowfish_encrypt(const void *state,
const uint8_t *in,
uint8_t *out,
size_t data_len);
int Blowfish_decrypt(const void *state,
const uint8_t *in,
uint8_t *out,
size_t data_len);
int Blowfish_stop_operation(void *state);
"""
)
def _create_base_cipher(dict_parameters):
"""This method instantiates and returns a smart pointer to
a low-level base cipher. It will absorb named parameters in
the process."""
try:
key = dict_parameters.pop("key")
except KeyError:
raise TypeError("Missing 'key' parameter")
if len(key) not in key_size:
raise ValueError("Incorrect Blowfish key length (%d bytes)" % len(key))
start_operation = _raw_blowfish_lib.Blowfish_start_operation
stop_operation = _raw_blowfish_lib.Blowfish_stop_operation
void_p = VoidPointer()
result = start_operation(c_uint8_ptr(key),
c_size_t(len(key)),
void_p.address_of())
if result:
raise ValueError("Error %X while instantiating the Blowfish cipher"
% result)
return SmartPointer(void_p.get(), stop_operation)
def new(key, mode, *args, **kwargs):
"""Create a new Blowfish cipher
:param key:
The secret key to use in the symmetric cipher.
Its length can vary from 5 to 56 bytes.
:type key: bytes, bytearray, memoryview
:param mode:
The chaining mode to use for encryption or decryption.
:type mode: One of the supported ``MODE_*`` constants
:Keyword Arguments:
* **iv** (*bytes*, *bytearray*, *memoryview*) --
(Only applicable for ``MODE_CBC``, ``MODE_CFB``, ``MODE_OFB``,
and ``MODE_OPENPGP`` modes).
The initialization vector to use for encryption or decryption.
For ``MODE_CBC``, ``MODE_CFB``, and ``MODE_OFB`` it must be 8 bytes long.
For ``MODE_OPENPGP`` mode only,
it must be 8 bytes long for encryption
and 10 bytes for decryption (in the latter case, it is
actually the *encrypted* IV which was prefixed to the ciphertext).
If not provided, a random byte string is generated (you must then
read its value with the :attr:`iv` attribute).
* **nonce** (*bytes*, *bytearray*, *memoryview*) --
(Only applicable for ``MODE_EAX`` and ``MODE_CTR``).
A value that must never be reused for any other encryption done
with this key.
For ``MODE_EAX`` there are no
restrictions on its length (recommended: **16** bytes).
For ``MODE_CTR``, its length must be in the range **[0..7]**.
If not provided for ``MODE_EAX``, a random byte string is generated (you
can read it back via the ``nonce`` attribute).
* **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 not specified, it will be assumed to be 8.
* **mac_len** : (*integer*) --
(Only ``MODE_EAX``)
Length of the authentication tag, in bytes.
It must be no longer than 8 (default).
* **initial_value** : (*integer*) --
(Only ``MODE_CTR``). The initial value for the counter within
the counter block. By default it is **0**.
:Return: a Blowfish object, of the applicable mode.
"""
return _create_cipher(sys.modules[__name__], key, mode, *args, **kwargs)
MODE_ECB = 1
MODE_CBC = 2
MODE_CFB = 3
MODE_OFB = 5
MODE_CTR = 6
MODE_OPENPGP = 7
MODE_EAX = 9
# Size of a data block (in bytes)
block_size = 8
# Size of a key (in bytes)
key_size = range(4, 56 + 1)

View File

@ -0,0 +1,159 @@
# -*- 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.
# ===================================================================
"""
Module's constants for the modes of operation supported with CAST:
:var MODE_ECB: :ref:`Electronic Code Book (ECB) <ecb_mode>`
:var MODE_CBC: :ref:`Cipher-Block Chaining (CBC) <cbc_mode>`
:var MODE_CFB: :ref:`Cipher FeedBack (CFB) <cfb_mode>`
:var MODE_OFB: :ref:`Output FeedBack (OFB) <ofb_mode>`
:var MODE_CTR: :ref:`CounTer Mode (CTR) <ctr_mode>`
:var MODE_OPENPGP: :ref:`OpenPGP Mode <openpgp_mode>`
:var MODE_EAX: :ref:`EAX Mode <eax_mode>`
"""
import sys
from Crypto.Cipher import _create_cipher
from Crypto.Util.py3compat import byte_string
from Crypto.Util._raw_api import (load_pycryptodome_raw_lib,
VoidPointer, SmartPointer,
c_size_t, c_uint8_ptr)
_raw_cast_lib = load_pycryptodome_raw_lib(
"Crypto.Cipher._raw_cast",
"""
int CAST_start_operation(const uint8_t key[],
size_t key_len,
void **pResult);
int CAST_encrypt(const void *state,
const uint8_t *in,
uint8_t *out,
size_t data_len);
int CAST_decrypt(const void *state,
const uint8_t *in,
uint8_t *out,
size_t data_len);
int CAST_stop_operation(void *state);
""")
def _create_base_cipher(dict_parameters):
"""This method instantiates and returns a handle to a low-level
base cipher. It will absorb named parameters in the process."""
try:
key = dict_parameters.pop("key")
except KeyError:
raise TypeError("Missing 'key' parameter")
if len(key) not in key_size:
raise ValueError("Incorrect CAST key length (%d bytes)" % len(key))
start_operation = _raw_cast_lib.CAST_start_operation
stop_operation = _raw_cast_lib.CAST_stop_operation
cipher = VoidPointer()
result = start_operation(c_uint8_ptr(key),
c_size_t(len(key)),
cipher.address_of())
if result:
raise ValueError("Error %X while instantiating the CAST cipher"
% result)
return SmartPointer(cipher.get(), stop_operation)
def new(key, mode, *args, **kwargs):
"""Create a new CAST cipher
:param key:
The secret key to use in the symmetric cipher.
Its length can vary from 5 to 16 bytes.
:type key: bytes, bytearray, memoryview
:param mode:
The chaining mode to use for encryption or decryption.
:type mode: One of the supported ``MODE_*`` constants
:Keyword Arguments:
* **iv** (*bytes*, *bytearray*, *memoryview*) --
(Only applicable for ``MODE_CBC``, ``MODE_CFB``, ``MODE_OFB``,
and ``MODE_OPENPGP`` modes).
The initialization vector to use for encryption or decryption.
For ``MODE_CBC``, ``MODE_CFB``, and ``MODE_OFB`` it must be 8 bytes long.
For ``MODE_OPENPGP`` mode only,
it must be 8 bytes long for encryption
and 10 bytes for decryption (in the latter case, it is
actually the *encrypted* IV which was prefixed to the ciphertext).
If not provided, a random byte string is generated (you must then
read its value with the :attr:`iv` attribute).
* **nonce** (*bytes*, *bytearray*, *memoryview*) --
(Only applicable for ``MODE_EAX`` and ``MODE_CTR``).
A value that must never be reused for any other encryption done
with this key.
For ``MODE_EAX`` there are no
restrictions on its length (recommended: **16** bytes).
For ``MODE_CTR``, its length must be in the range **[0..7]**.
If not provided for ``MODE_EAX``, a random byte string is generated (you
can read it back via the ``nonce`` attribute).
* **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 not specified, it will be assumed to be 8.
* **mac_len** : (*integer*) --
(Only ``MODE_EAX``)
Length of the authentication tag, in bytes.
It must be no longer than 8 (default).
* **initial_value** : (*integer*) --
(Only ``MODE_CTR``). The initial value for the counter within
the counter block. By default it is **0**.
:Return: a CAST object, of the applicable mode.
"""
return _create_cipher(sys.modules[__name__], key, mode, *args, **kwargs)
MODE_ECB = 1
MODE_CBC = 2
MODE_CFB = 3
MODE_OFB = 5
MODE_CTR = 6
MODE_OPENPGP = 7
MODE_EAX = 9
# Size of a data block (in bytes)
block_size = 8
# Size of a key (in bytes)
key_size = range(5, 16 + 1)

View File

@ -0,0 +1,287 @@
# ===================================================================
#
# Copyright (c) 2014, Legrandin <helderijs@gmail.com>
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
#
# 1. Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# 2. Redistributions in binary form must reproduce the above copyright
# notice, this list of conditions and the following disclaimer in
# the documentation and/or other materials provided with the
# distribution.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
# COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.
# ===================================================================
from Crypto.Random import get_random_bytes
from Crypto.Util.py3compat import _copy_bytes
from Crypto.Util._raw_api import (load_pycryptodome_raw_lib,
create_string_buffer,
get_raw_buffer, VoidPointer,
SmartPointer, c_size_t,
c_uint8_ptr, c_ulong,
is_writeable_buffer)
_raw_chacha20_lib = load_pycryptodome_raw_lib("Crypto.Cipher._chacha20",
"""
int chacha20_init(void **pState,
const uint8_t *key,
size_t keySize,
const uint8_t *nonce,
size_t nonceSize);
int chacha20_destroy(void *state);
int chacha20_encrypt(void *state,
const uint8_t in[],
uint8_t out[],
size_t len);
int chacha20_seek(void *state,
unsigned long block_high,
unsigned long block_low,
unsigned offset);
int hchacha20( const uint8_t key[32],
const uint8_t nonce16[16],
uint8_t subkey[32]);
""")
def _HChaCha20(key, nonce):
assert(len(key) == 32)
assert(len(nonce) == 16)
subkey = bytearray(32)
result = _raw_chacha20_lib.hchacha20(
c_uint8_ptr(key),
c_uint8_ptr(nonce),
c_uint8_ptr(subkey))
if result:
raise ValueError("Error %d when deriving subkey with HChaCha20" % result)
return subkey
class ChaCha20Cipher(object):
"""ChaCha20 (or XChaCha20) cipher object.
Do not create it directly. Use :py:func:`new` instead.
:var nonce: The nonce with length 8, 12 or 24 bytes
:vartype nonce: bytes
"""
block_size = 1
def __init__(self, key, nonce):
"""Initialize a ChaCha20/XChaCha20 cipher object
See also `new()` at the module level."""
self.nonce = _copy_bytes(None, None, nonce)
# XChaCha20 requires a key derivation with HChaCha20
# See 2.3 in https://tools.ietf.org/html/draft-arciszewski-xchacha-03
if len(nonce) == 24:
key = _HChaCha20(key, nonce[:16])
nonce = b'\x00' * 4 + nonce[16:]
self._name = "XChaCha20"
else:
self._name = "ChaCha20"
nonce = self.nonce
self._next = ( self.encrypt, self.decrypt )
self._state = VoidPointer()
result = _raw_chacha20_lib.chacha20_init(
self._state.address_of(),
c_uint8_ptr(key),
c_size_t(len(key)),
nonce,
c_size_t(len(nonce)))
if result:
raise ValueError("Error %d instantiating a %s cipher" % (result,
self._name))
self._state = SmartPointer(self._state.get(),
_raw_chacha20_lib.chacha20_destroy)
def encrypt(self, plaintext, output=None):
"""Encrypt a piece of data.
Args:
plaintext(bytes/bytearray/memoryview): The data to encrypt, of any size.
Keyword Args:
output(bytes/bytearray/memoryview): The location where the ciphertext
is written to. If ``None``, the ciphertext is returned.
Returns:
If ``output`` is ``None``, the ciphertext is returned as ``bytes``.
Otherwise, ``None``.
"""
if self.encrypt not in self._next:
raise TypeError("Cipher object can only be used for decryption")
self._next = ( self.encrypt, )
return self._encrypt(plaintext, output)
def _encrypt(self, plaintext, output):
"""Encrypt without FSM checks"""
if output is None:
ciphertext = create_string_buffer(len(plaintext))
else:
ciphertext = output
if not is_writeable_buffer(output):
raise TypeError("output must be a bytearray or a writeable memoryview")
if len(plaintext) != len(output):
raise ValueError("output must have the same length as the input"
" (%d bytes)" % len(plaintext))
result = _raw_chacha20_lib.chacha20_encrypt(
self._state.get(),
c_uint8_ptr(plaintext),
c_uint8_ptr(ciphertext),
c_size_t(len(plaintext)))
if result:
raise ValueError("Error %d while encrypting with %s" % (result, self._name))
if output is None:
return get_raw_buffer(ciphertext)
else:
return None
def decrypt(self, ciphertext, output=None):
"""Decrypt a piece of data.
Args:
ciphertext(bytes/bytearray/memoryview): The data to decrypt, of any size.
Keyword Args:
output(bytes/bytearray/memoryview): The location where the plaintext
is written to. If ``None``, the plaintext is returned.
Returns:
If ``output`` is ``None``, the plaintext is returned as ``bytes``.
Otherwise, ``None``.
"""
if self.decrypt not in self._next:
raise TypeError("Cipher object can only be used for encryption")
self._next = ( self.decrypt, )
try:
return self._encrypt(ciphertext, output)
except ValueError as e:
raise ValueError(str(e).replace("enc", "dec"))
def seek(self, position):
"""Seek to a certain position in the key stream.
Args:
position (integer):
The absolute position within the key stream, in bytes.
"""
position, offset = divmod(position, 64)
block_low = position & 0xFFFFFFFF
block_high = position >> 32
result = _raw_chacha20_lib.chacha20_seek(
self._state.get(),
c_ulong(block_high),
c_ulong(block_low),
offset
)
if result:
raise ValueError("Error %d while seeking with %s" % (result, self._name))
def _derive_Poly1305_key_pair(key, nonce):
"""Derive a tuple (r, s, nonce) for a Poly1305 MAC.
If nonce is ``None``, a new 12-byte nonce is generated.
"""
if len(key) != 32:
raise ValueError("Poly1305 with ChaCha20 requires a 32-byte key")
if nonce is None:
padded_nonce = nonce = get_random_bytes(12)
elif len(nonce) == 8:
# See RFC7538, 2.6: [...] ChaCha20 as specified here requires a 96-bit
# nonce. So if the provided nonce is only 64-bit, then the first 32
# bits of the nonce will be set to a constant number.
# This will usually be zero, but for protocols with multiple senders it may be
# different for each sender, but should be the same for all
# invocations of the function with the same key by a particular
# sender.
padded_nonce = b'\x00\x00\x00\x00' + nonce
elif len(nonce) == 12:
padded_nonce = nonce
else:
raise ValueError("Poly1305 with ChaCha20 requires an 8- or 12-byte nonce")
rs = new(key=key, nonce=padded_nonce).encrypt(b'\x00' * 32)
return rs[:16], rs[16:], nonce
def new(**kwargs):
"""Create a new ChaCha20 or XChaCha20 cipher
Keyword Args:
key (bytes/bytearray/memoryview): The secret key to use.
It must be 32 bytes long.
nonce (bytes/bytearray/memoryview): A mandatory value that
must never be reused for any other encryption
done with this key.
For ChaCha20, it must be 8 or 12 bytes long.
For XChaCha20, it must be 24 bytes long.
If not provided, 8 bytes will be randomly generated
(you can find them back in the ``nonce`` attribute).
:Return: a :class:`Crypto.Cipher.ChaCha20.ChaCha20Cipher` object
"""
try:
key = kwargs.pop("key")
except KeyError as e:
raise TypeError("Missing parameter %s" % e)
nonce = kwargs.pop("nonce", None)
if nonce is None:
nonce = get_random_bytes(8)
if len(key) != 32:
raise ValueError("ChaCha20/XChaCha20 key must be 32 bytes long")
if len(nonce) not in (8, 12, 24):
raise ValueError("Nonce must be 8/12 bytes(ChaCha20) or 24 bytes (XChaCha20)")
if kwargs:
raise TypeError("Unknown parameters: " + str(kwargs))
return ChaCha20Cipher(key, nonce)
# Size of a data block (in bytes)
block_size = 1
# Size of a key (in bytes)
key_size = 32

View File

@ -0,0 +1,336 @@
# ===================================================================
#
# Copyright (c) 2018, Helder Eijs <helderijs@gmail.com>
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
#
# 1. Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# 2. Redistributions in binary form must reproduce the above copyright
# notice, this list of conditions and the following disclaimer in
# the documentation and/or other materials provided with the
# distribution.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
# COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.
# ===================================================================
from binascii import unhexlify
from Crypto.Cipher import ChaCha20
from Crypto.Cipher.ChaCha20 import _HChaCha20
from Crypto.Hash import Poly1305, BLAKE2s
from Crypto.Random import get_random_bytes
from Crypto.Util.number import long_to_bytes
from Crypto.Util.py3compat import _copy_bytes, bord
from Crypto.Util._raw_api import is_buffer
def _enum(**enums):
return type('Enum', (), enums)
_CipherStatus = _enum(PROCESSING_AUTH_DATA=1,
PROCESSING_CIPHERTEXT=2,
PROCESSING_DONE=3)
class ChaCha20Poly1305Cipher(object):
"""ChaCha20-Poly1305 and XChaCha20-Poly1305 cipher object.
Do not create it directly. Use :py:func:`new` instead.
:var nonce: The nonce with length 8, 12 or 24 bytes
:vartype nonce: byte string
"""
def __init__(self, key, nonce):
"""Initialize a ChaCha20-Poly1305 AEAD cipher object
See also `new()` at the module level."""
self.nonce = _copy_bytes(None, None, nonce)
self._next = (self.update, self.encrypt, self.decrypt, self.digest,
self.verify)
self._authenticator = Poly1305.new(key=key, nonce=nonce, cipher=ChaCha20)
self._cipher = ChaCha20.new(key=key, nonce=nonce)
self._cipher.seek(64) # Block counter starts at 1
self._len_aad = 0
self._len_ct = 0
self._mac_tag = None
self._status = _CipherStatus.PROCESSING_AUTH_DATA
def update(self, data):
"""Protect the associated data.
Associated data (also known as *additional authenticated data* - AAD)
is the piece of the message that must stay in the clear, while
still allowing the receiver to verify its integrity.
An example is packet headers.
The associated data (possibly split into multiple segments) is
fed into :meth:`update` before any call to :meth:`decrypt` or :meth:`encrypt`.
If there is no associated data, :meth:`update` is not called.
:param bytes/bytearray/memoryview assoc_data:
A piece of associated data. There are no restrictions on its size.
"""
if self.update not in self._next:
raise TypeError("update() method cannot be called")
self._len_aad += len(data)
self._authenticator.update(data)
def _pad_aad(self):
assert(self._status == _CipherStatus.PROCESSING_AUTH_DATA)
if self._len_aad & 0x0F:
self._authenticator.update(b'\x00' * (16 - (self._len_aad & 0x0F)))
self._status = _CipherStatus.PROCESSING_CIPHERTEXT
def encrypt(self, plaintext, output=None):
"""Encrypt a piece of data.
Args:
plaintext(bytes/bytearray/memoryview): The data to encrypt, of any size.
Keyword Args:
output(bytes/bytearray/memoryview): The location where the ciphertext
is written to. If ``None``, the ciphertext is returned.
Returns:
If ``output`` is ``None``, the ciphertext is returned as ``bytes``.
Otherwise, ``None``.
"""
if self.encrypt not in self._next:
raise TypeError("encrypt() method cannot be called")
if self._status == _CipherStatus.PROCESSING_AUTH_DATA:
self._pad_aad()
self._next = (self.encrypt, self.digest)
result = self._cipher.encrypt(plaintext, output=output)
self._len_ct += len(plaintext)
if output is None:
self._authenticator.update(result)
else:
self._authenticator.update(output)
return result
def decrypt(self, ciphertext, output=None):
"""Decrypt a piece of data.
Args:
ciphertext(bytes/bytearray/memoryview): The data to decrypt, of any size.
Keyword Args:
output(bytes/bytearray/memoryview): The location where the plaintext
is written to. If ``None``, the plaintext is returned.
Returns:
If ``output`` is ``None``, the plaintext is returned as ``bytes``.
Otherwise, ``None``.
"""
if self.decrypt not in self._next:
raise TypeError("decrypt() method cannot be called")
if self._status == _CipherStatus.PROCESSING_AUTH_DATA:
self._pad_aad()
self._next = (self.decrypt, self.verify)
self._len_ct += len(ciphertext)
self._authenticator.update(ciphertext)
return self._cipher.decrypt(ciphertext, output=output)
def _compute_mac(self):
"""Finalize the cipher (if not done already) and return the MAC."""
if self._mac_tag:
assert(self._status == _CipherStatus.PROCESSING_DONE)
return self._mac_tag
assert(self._status != _CipherStatus.PROCESSING_DONE)
if self._status == _CipherStatus.PROCESSING_AUTH_DATA:
self._pad_aad()
if self._len_ct & 0x0F:
self._authenticator.update(b'\x00' * (16 - (self._len_ct & 0x0F)))
self._status = _CipherStatus.PROCESSING_DONE
self._authenticator.update(long_to_bytes(self._len_aad, 8)[::-1])
self._authenticator.update(long_to_bytes(self._len_ct, 8)[::-1])
self._mac_tag = self._authenticator.digest()
return self._mac_tag
def digest(self):
"""Compute the *binary* authentication tag (MAC).
:Return: the MAC tag, as 16 ``bytes``.
"""
if self.digest not in self._next:
raise TypeError("digest() method cannot be called")
self._next = (self.digest,)
return self._compute_mac()
def hexdigest(self):
"""Compute the *printable* authentication tag (MAC).
This method is like :meth:`digest`.
:Return: the MAC tag, as a hexadecimal string.
"""
return "".join(["%02x" % bord(x) for x in self.digest()])
def verify(self, received_mac_tag):
"""Validate the *binary* authentication tag (MAC).
The receiver invokes this method at the very end, to
check if the associated data (if any) and the decrypted
messages are valid.
:param bytes/bytearray/memoryview received_mac_tag:
This is the 16-byte *binary* MAC, as received from the sender.
:Raises ValueError:
if the MAC does not match. The message has been tampered with
or the key is incorrect.
"""
if self.verify not in self._next:
raise TypeError("verify() cannot be called"
" when encrypting a message")
self._next = (self.verify,)
secret = get_random_bytes(16)
self._compute_mac()
mac1 = BLAKE2s.new(digest_bits=160, key=secret,
data=self._mac_tag)
mac2 = BLAKE2s.new(digest_bits=160, key=secret,
data=received_mac_tag)
if mac1.digest() != mac2.digest():
raise ValueError("MAC check failed")
def hexverify(self, hex_mac_tag):
"""Validate the *printable* authentication tag (MAC).
This method is like :meth:`verify`.
:param string hex_mac_tag:
This is the *printable* MAC.
:Raises ValueError:
if the MAC does not match. The message has been tampered with
or the key is incorrect.
"""
self.verify(unhexlify(hex_mac_tag))
def encrypt_and_digest(self, plaintext):
"""Perform :meth:`encrypt` and :meth:`digest` in one step.
:param plaintext: The data to encrypt, of any size.
:type plaintext: bytes/bytearray/memoryview
:return: a tuple with two ``bytes`` objects:
- the ciphertext, of equal length as the plaintext
- the 16-byte MAC tag
"""
return self.encrypt(plaintext), self.digest()
def decrypt_and_verify(self, ciphertext, received_mac_tag):
"""Perform :meth:`decrypt` and :meth:`verify` in one step.
:param ciphertext: The piece of data to decrypt.
:type ciphertext: bytes/bytearray/memoryview
:param bytes received_mac_tag:
This is the 16-byte *binary* MAC, as received from the sender.
:return: the decrypted data (as ``bytes``)
:raises ValueError:
if the MAC does not match. The message has been tampered with
or the key is incorrect.
"""
plaintext = self.decrypt(ciphertext)
self.verify(received_mac_tag)
return plaintext
def new(**kwargs):
"""Create a new ChaCha20-Poly1305 or XChaCha20-Poly1305 AEAD cipher.
:keyword key: The secret key to use. It must be 32 bytes long.
:type key: byte string
:keyword nonce:
A value that must never be reused for any other encryption
done with this key.
For ChaCha20-Poly1305, it must be 8 or 12 bytes long.
For XChaCha20-Poly1305, it must be 24 bytes long.
If not provided, 12 ``bytes`` will be generated randomly
(you can find them back in the ``nonce`` attribute).
:type nonce: bytes, bytearray, memoryview
:Return: a :class:`Crypto.Cipher.ChaCha20.ChaCha20Poly1305Cipher` object
"""
try:
key = kwargs.pop("key")
except KeyError as e:
raise TypeError("Missing parameter %s" % e)
self._len_ct += len(plaintext)
if len(key) != 32:
raise ValueError("Key must be 32 bytes long")
nonce = kwargs.pop("nonce", None)
if nonce is None:
nonce = get_random_bytes(12)
if len(nonce) in (8, 12):
pass
elif len(nonce) == 24:
key = _HChaCha20(key, nonce[:16])
nonce = b'\x00\x00\x00\x00' + nonce[16:]
else:
raise ValueError("Nonce must be 8, 12 or 24 bytes long")
if not is_buffer(nonce):
raise TypeError("nonce must be bytes, bytearray or memoryview")
if kwargs:
raise TypeError("Unknown parameters: " + str(kwargs))
return ChaCha20Poly1305Cipher(key, nonce)
# Size of a key (in bytes)
key_size = 32

View File

@ -0,0 +1,158 @@
# -*- 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.
# ===================================================================
"""
Module's constants for the modes of operation supported with Single DES:
:var MODE_ECB: :ref:`Electronic Code Book (ECB) <ecb_mode>`
:var MODE_CBC: :ref:`Cipher-Block Chaining (CBC) <cbc_mode>`
:var MODE_CFB: :ref:`Cipher FeedBack (CFB) <cfb_mode>`
:var MODE_OFB: :ref:`Output FeedBack (OFB) <ofb_mode>`
:var MODE_CTR: :ref:`CounTer Mode (CTR) <ctr_mode>`
:var MODE_OPENPGP: :ref:`OpenPGP Mode <openpgp_mode>`
:var MODE_EAX: :ref:`EAX Mode <eax_mode>`
"""
import sys
from Crypto.Cipher import _create_cipher
from Crypto.Util.py3compat import byte_string
from Crypto.Util._raw_api import (load_pycryptodome_raw_lib,
VoidPointer, SmartPointer,
c_size_t, c_uint8_ptr)
_raw_des_lib = load_pycryptodome_raw_lib(
"Crypto.Cipher._raw_des",
"""
int DES_start_operation(const uint8_t key[],
size_t key_len,
void **pResult);
int DES_encrypt(const void *state,
const uint8_t *in,
uint8_t *out,
size_t data_len);
int DES_decrypt(const void *state,
const uint8_t *in,
uint8_t *out,
size_t data_len);
int DES_stop_operation(void *state);
""")
def _create_base_cipher(dict_parameters):
"""This method instantiates and returns a handle to a low-level
base cipher. It will absorb named parameters in the process."""
try:
key = dict_parameters.pop("key")
except KeyError:
raise TypeError("Missing 'key' parameter")
if len(key) != key_size:
raise ValueError("Incorrect DES key length (%d bytes)" % len(key))
start_operation = _raw_des_lib.DES_start_operation
stop_operation = _raw_des_lib.DES_stop_operation
cipher = VoidPointer()
result = start_operation(c_uint8_ptr(key),
c_size_t(len(key)),
cipher.address_of())
if result:
raise ValueError("Error %X while instantiating the DES cipher"
% result)
return SmartPointer(cipher.get(), stop_operation)
def new(key, mode, *args, **kwargs):
"""Create a new DES cipher.
:param key:
The secret key to use in the symmetric cipher.
It must be 8 byte long. The parity bits will be ignored.
:type key: bytes/bytearray/memoryview
:param mode:
The chaining mode to use for encryption or decryption.
:type mode: One of the supported ``MODE_*`` constants
:Keyword Arguments:
* **iv** (*byte string*) --
(Only applicable for ``MODE_CBC``, ``MODE_CFB``, ``MODE_OFB``,
and ``MODE_OPENPGP`` modes).
The initialization vector to use for encryption or decryption.
For ``MODE_CBC``, ``MODE_CFB``, and ``MODE_OFB`` it must be 8 bytes long.
For ``MODE_OPENPGP`` mode only,
it must be 8 bytes long for encryption
and 10 bytes for decryption (in the latter case, it is
actually the *encrypted* IV which was prefixed to the ciphertext).
If not provided, a random byte string is generated (you must then
read its value with the :attr:`iv` attribute).
* **nonce** (*byte string*) --
(Only applicable for ``MODE_EAX`` and ``MODE_CTR``).
A value that must never be reused for any other encryption done
with this key.
For ``MODE_EAX`` there are no
restrictions on its length (recommended: **16** bytes).
For ``MODE_CTR``, its length must be in the range **[0..7]**.
If not provided for ``MODE_EAX``, a random byte string is generated (you
can read it back via the ``nonce`` attribute).
* **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 not specified, it will be assumed to be 8.
* **mac_len** : (*integer*) --
(Only ``MODE_EAX``)
Length of the authentication tag, in bytes.
It must be no longer than 8 (default).
* **initial_value** : (*integer*) --
(Only ``MODE_CTR``). The initial value for the counter within
the counter block. By default it is **0**.
:Return: a DES object, of the applicable mode.
"""
return _create_cipher(sys.modules[__name__], key, mode, *args, **kwargs)
MODE_ECB = 1
MODE_CBC = 2
MODE_CFB = 3
MODE_OFB = 5
MODE_CTR = 6
MODE_OPENPGP = 7
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,187 @@
# -*- 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.
# ===================================================================
"""
Module's constants for the modes of operation supported with Triple DES:
:var MODE_ECB: :ref:`Electronic Code Book (ECB) <ecb_mode>`
:var MODE_CBC: :ref:`Cipher-Block Chaining (CBC) <cbc_mode>`
:var MODE_CFB: :ref:`Cipher FeedBack (CFB) <cfb_mode>`
:var MODE_OFB: :ref:`Output FeedBack (OFB) <ofb_mode>`
:var MODE_CTR: :ref:`CounTer Mode (CTR) <ctr_mode>`
:var MODE_OPENPGP: :ref:`OpenPGP Mode <openpgp_mode>`
:var MODE_EAX: :ref:`EAX Mode <eax_mode>`
"""
import sys
from Crypto.Cipher import _create_cipher
from Crypto.Util.py3compat import byte_string, bchr, bord, bstr
from Crypto.Util._raw_api import (load_pycryptodome_raw_lib,
VoidPointer, SmartPointer,
c_size_t)
_raw_des3_lib = load_pycryptodome_raw_lib(
"Crypto.Cipher._raw_des3",
"""
int DES3_start_operation(const uint8_t key[],
size_t key_len,
void **pResult);
int DES3_encrypt(const void *state,
const uint8_t *in,
uint8_t *out,
size_t data_len);
int DES3_decrypt(const void *state,
const uint8_t *in,
uint8_t *out,
size_t data_len);
int DES3_stop_operation(void *state);
""")
def adjust_key_parity(key_in):
"""Set the parity bits in a TDES key.
:param key_in: the TDES key whose bits need to be adjusted
:type key_in: byte string
:returns: a copy of ``key_in``, with the parity bits correctly set
:rtype: byte string
:raises ValueError: if the TDES key is not 16 or 24 bytes long
:raises ValueError: if the TDES key degenerates into Single DES
"""
def parity_byte(key_byte):
parity = 1
for i in range(1, 8):
parity ^= (key_byte >> i) & 1
return (key_byte & 0xFE) | parity
if len(key_in) not in key_size:
raise ValueError("Not a valid TDES key")
key_out = b"".join([ bchr(parity_byte(bord(x))) for x in key_in ])
if key_out[:8] == key_out[8:16] or key_out[-16:-8] == key_out[-8:]:
raise ValueError("Triple DES key degenerates to single DES")
return key_out
def _create_base_cipher(dict_parameters):
"""This method instantiates and returns a handle to a low-level base cipher.
It will absorb named parameters in the process."""
try:
key_in = dict_parameters.pop("key")
except KeyError:
raise TypeError("Missing 'key' parameter")
key = adjust_key_parity(bstr(key_in))
start_operation = _raw_des3_lib.DES3_start_operation
stop_operation = _raw_des3_lib.DES3_stop_operation
cipher = VoidPointer()
result = start_operation(key,
c_size_t(len(key)),
cipher.address_of())
if result:
raise ValueError("Error %X while instantiating the TDES cipher"
% result)
return SmartPointer(cipher.get(), stop_operation)
def new(key, mode, *args, **kwargs):
"""Create a new Triple DES cipher.
:param key:
The secret key to use in the symmetric cipher.
It must be 16 or 24 byte long. The parity bits will be ignored.
:type key: bytes/bytearray/memoryview
:param mode:
The chaining mode to use for encryption or decryption.
:type mode: One of the supported ``MODE_*`` constants
:Keyword Arguments:
* **iv** (*bytes*, *bytearray*, *memoryview*) --
(Only applicable for ``MODE_CBC``, ``MODE_CFB``, ``MODE_OFB``,
and ``MODE_OPENPGP`` modes).
The initialization vector to use for encryption or decryption.
For ``MODE_CBC``, ``MODE_CFB``, and ``MODE_OFB`` it must be 8 bytes long.
For ``MODE_OPENPGP`` mode only,
it must be 8 bytes long for encryption
and 10 bytes for decryption (in the latter case, it is
actually the *encrypted* IV which was prefixed to the ciphertext).
If not provided, a random byte string is generated (you must then
read its value with the :attr:`iv` attribute).
* **nonce** (*bytes*, *bytearray*, *memoryview*) --
(Only applicable for ``MODE_EAX`` and ``MODE_CTR``).
A value that must never be reused for any other encryption done
with this key.
For ``MODE_EAX`` there are no
restrictions on its length (recommended: **16** bytes).
For ``MODE_CTR``, its length must be in the range **[0..7]**.
If not provided for ``MODE_EAX``, a random byte string is generated (you
can read it back via the ``nonce`` attribute).
* **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 not specified, it will be assumed to be 8.
* **mac_len** : (*integer*) --
(Only ``MODE_EAX``)
Length of the authentication tag, in bytes.
It must be no longer than 8 (default).
* **initial_value** : (*integer*) --
(Only ``MODE_CTR``). The initial value for the counter within
the counter block. By default it is **0**.
:Return: a Triple DES object, of the applicable mode.
"""
return _create_cipher(sys.modules[__name__], key, mode, *args, **kwargs)
MODE_ECB = 1
MODE_CBC = 2
MODE_CFB = 3
MODE_OFB = 5
MODE_CTR = 6
MODE_OPENPGP = 7
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,239 @@
# -*- 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.
# ===================================================================
from Crypto.Signature.pss import MGF1
import Crypto.Hash.SHA1
from Crypto.Util.py3compat import bord, _copy_bytes
import Crypto.Util.number
from Crypto.Util.number import ceil_div, bytes_to_long, long_to_bytes
from Crypto.Util.strxor import strxor
from Crypto import Random
class PKCS1OAEP_Cipher:
"""Cipher object for PKCS#1 v1.5 OAEP.
Do not create directly: use :func:`new` instead."""
def __init__(self, key, hashAlgo, mgfunc, label, randfunc):
"""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 consistent with ``hashAlgo`` is used (a safe choice).
label : bytes/bytearray/memoryview
A label to apply to this particular encryption. If not specified,
an empty string is used. Specifying a label does not improve
security.
randfunc : callable
A function that returns random bytes.
: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: MGF1(x,y,self._hashObj)
self._label = _copy_bytes(None, None, label)
self._randfunc = randfunc
def can_encrypt(self):
"""Legacy function to check if you can call :meth:`encrypt`.
.. deprecated:: 3.0"""
return self._key.can_encrypt()
def can_decrypt(self):
"""Legacy function to check if you can call :meth:`decrypt`.
.. deprecated:: 3.0"""
return self._key.can_decrypt()
def encrypt(self, message):
"""Encrypt a message with PKCS#1 OAEP.
:param message:
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.
For instance, if you use RSA 2048 and SHA-256, the longest message
you can encrypt is 190 byte long.
:type message: bytes/bytearray/memoryview
:returns: The ciphertext, as large as the RSA modulus.
:rtype: bytes
:raises ValueError:
if the message is too long.
"""
# 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 = b'\x00' * ps_len
# Step 2c
db = lHash + ps + b'\x01' + _copy_bytes(None, None, message)
# Step 2d
ros = self._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 = b'\x00' + maskedSeed + maskedDB
# Step 3a (OS2IP)
em_int = bytes_to_long(em)
# Step 3b (RSAEP)
m_int = self._key._encrypt(em_int)
# Step 3c (I2OSP)
c = long_to_bytes(m_int, k)
return c
def decrypt(self, ciphertext):
"""Decrypt a message with PKCS#1 OAEP.
:param ciphertext: The encrypted message.
:type ciphertext: bytes/bytearray/memoryview
:returns: The original message (plaintext).
:rtype: bytes
:raises ValueError:
if the ciphertext has the wrong length, or if decryption
fails the integrity check (in which case, the decryption
key is probably wrong).
:raises TypeError:
if the RSA key has no private half (i.e. you are trying
to decrypt using a public key).
"""
# 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(ciphertext) != k or k<hLen+2:
raise ValueError("Ciphertext with incorrect length.")
# Step 2a (O2SIP)
ct_int = bytes_to_long(ciphertext)
# Step 2b (RSADP)
m_int = self._key._decrypt(ct_int)
# Complete step 2c (I2OSP)
em = long_to_bytes(m_int, k)
# 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
one_pos = hLen + db[hLen:].find(b'\x01')
lHash1 = db[:hLen]
invalid = bord(y) | int(one_pos < hLen)
hash_compare = strxor(lHash1, lHash)
for x in hash_compare:
invalid |= bord(x)
for x in db[hLen:one_pos]:
invalid |= bord(x)
if invalid != 0:
raise ValueError("Incorrect decryption.")
# Step 4
return db[one_pos + 1:]
def new(key, hashAlgo=None, mgfunc=None, label=b'', randfunc=None):
"""Return a cipher object :class:`PKCS1OAEP_Cipher` that can be used to perform PKCS#1 OAEP encryption or decryption.
:param key:
The key object to use to encrypt or decrypt the message.
Decryption is only possible with a private RSA key.
:type key: RSA key object
:param hashAlgo:
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.
:type hashAlgo: hash object
:param mgfunc:
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 consistent with ``hashAlgo`` is used (a safe choice).
:type mgfunc: callable
:param label:
A label to apply to this particular encryption. If not specified,
an empty string is used. Specifying a label does not improve
security.
:type label: bytes/bytearray/memoryview
:param randfunc:
A function that returns random bytes.
The default is `Random.get_random_bytes`.
:type randfunc: callable
"""
if randfunc is None:
randfunc = Random.get_random_bytes
return PKCS1OAEP_Cipher(key, hashAlgo, mgfunc, label, randfunc)

View File

@ -0,0 +1,217 @@
# -*- 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.
# ===================================================================
__all__ = ['new', 'PKCS115_Cipher']
from Crypto import Random
from Crypto.Util.number import bytes_to_long, long_to_bytes
from Crypto.Util.py3compat import bord, is_bytes, _copy_bytes
from Crypto.Util._raw_api import (load_pycryptodome_raw_lib, c_size_t,
c_uint8_ptr)
_raw_pkcs1_decode = load_pycryptodome_raw_lib("Crypto.Cipher._pkcs1_decode",
"""
int pkcs1_decode(const uint8_t *em, size_t len_em,
const uint8_t *sentinel, size_t len_sentinel,
size_t expected_pt_len,
uint8_t *output);
""")
def _pkcs1_decode(em, sentinel, expected_pt_len, output):
if len(em) != len(output):
raise ValueError("Incorrect output length")
ret = _raw_pkcs1_decode.pkcs1_decode(c_uint8_ptr(em),
c_size_t(len(em)),
c_uint8_ptr(sentinel),
c_size_t(len(sentinel)),
c_size_t(expected_pt_len),
c_uint8_ptr(output))
return ret
class PKCS115_Cipher:
"""This cipher can perform PKCS#1 v1.5 RSA encryption or decryption.
Do not instantiate directly. Use :func:`Crypto.Cipher.PKCS1_v1_5.new` instead."""
def __init__(self, key, randfunc):
"""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.
randfunc : callable
Function that returns random bytes.
"""
self._key = key
self._randfunc = randfunc
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 it is specified in
`section 7.2.1 of RFC8017
<https://tools.ietf.org/html/rfc8017#page-28>`_.
:param message:
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.
:type message: bytes/bytearray/memoryview
:Returns: A byte string, the ciphertext in which the message is encrypted.
It is as long as the RSA modulus (in bytes).
:Raises ValueError:
If the RSA key length is not sufficiently long to deal with the given
message.
"""
# See 7.2.1 in RFC8017
k = self._key.size_in_bytes()
mLen = len(message)
# Step 1
if mLen > k - 11:
raise ValueError("Plaintext is too long.")
# Step 2a
ps = []
while len(ps) != k - mLen - 3:
new_byte = self._randfunc(1)
if bord(new_byte[0]) == 0x00:
continue
ps.append(new_byte)
ps = b"".join(ps)
assert(len(ps) == k - mLen - 3)
# Step 2b
em = b'\x00\x02' + ps + b'\x00' + _copy_bytes(None, None, message)
# Step 3a (OS2IP)
em_int = bytes_to_long(em)
# Step 3b (RSAEP)
m_int = self._key._encrypt(em_int)
# Step 3c (I2OSP)
c = long_to_bytes(m_int, k)
return c
def decrypt(self, ciphertext, sentinel, expected_pt_len=0):
r"""Decrypt a PKCS#1 v1.5 ciphertext.
This is the function ``RSAES-PKCS1-V1_5-DECRYPT`` specified in
`section 7.2.2 of RFC8017
<https://tools.ietf.org/html/rfc8017#page-29>`_.
Args:
ciphertext (bytes/bytearray/memoryview):
The ciphertext that contains the message to recover.
sentinel (any type):
The object to return whenever an error is detected.
expected_pt_len (integer):
The length the plaintext is known to have, or 0 if unknown.
Returns (byte string):
It is either the original message or the ``sentinel`` (in case of an error).
.. warning::
PKCS#1 v1.5 decryption is intrinsically vulnerable to timing
attacks (see `Bleichenbacher's`__ attack).
**Use PKCS#1 OAEP instead**.
This implementation attempts to mitigate the risk
with some constant-time constructs.
However, they are not sufficient by themselves: the type of protocol you
implement and the way you handle errors make a big difference.
Specifically, you should make it very hard for the (malicious)
party that submitted the ciphertext to quickly understand if decryption
succeeded or not.
To this end, it is recommended that your protocol only encrypts
plaintexts of fixed length (``expected_pt_len``),
that ``sentinel`` is a random byte string of the same length,
and that processing continues for as long
as possible even if ``sentinel`` is returned (i.e. in case of
incorrect decryption).
.. __: http://www.bell-labs.com/user/bleichen/papers/pkcs.ps
"""
# See 7.2.2 in RFC8017
k = self._key.size_in_bytes()
# Step 1
if len(ciphertext) != k:
raise ValueError("Ciphertext with incorrect length (not %d bytes)" % k)
# Step 2a (O2SIP)
ct_int = bytes_to_long(ciphertext)
# Step 2b (RSADP)
m_int = self._key._decrypt(ct_int)
# Complete step 2c (I2OSP)
em = long_to_bytes(m_int, k)
# Step 3 (not constant time when the sentinel is not a byte string)
output = bytearray(k)
if not is_bytes(sentinel) or len(sentinel) > k:
size = _pkcs1_decode(em, b'', expected_pt_len, output)
if size < 0:
return sentinel
else:
return output[size:]
# Step 3 (somewhat constant time)
size = _pkcs1_decode(em, sentinel, expected_pt_len, output)
return output[size:]
def new(key, randfunc=None):
"""Create a cipher for performing PKCS#1 v1.5 encryption or decryption.
:param key:
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.
:type key: RSA key object
:param randfunc:
Function that return random bytes.
The default is :func:`Crypto.Random.get_random_bytes`.
:type randfunc: callable
:returns: A cipher object `PKCS115_Cipher`.
"""
if randfunc is None:
randfunc = Random.get_random_bytes
return PKCS115_Cipher(key, randfunc)

View File

@ -0,0 +1,167 @@
# -*- coding: utf-8 -*-
#
# Cipher/Salsa20.py : Salsa20 stream cipher (http://cr.yp.to/snuffle.html)
#
# Contributed by Fabrizio Tarizzo <fabrizio@fabriziotarizzo.org>.
#
# ===================================================================
# 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 Crypto.Util.py3compat import _copy_bytes
from Crypto.Util._raw_api import (load_pycryptodome_raw_lib,
create_string_buffer,
get_raw_buffer, VoidPointer,
SmartPointer, c_size_t,
c_uint8_ptr, is_writeable_buffer)
from Crypto.Random import get_random_bytes
_raw_salsa20_lib = load_pycryptodome_raw_lib("Crypto.Cipher._Salsa20",
"""
int Salsa20_stream_init(uint8_t *key, size_t keylen,
uint8_t *nonce, size_t nonce_len,
void **pSalsaState);
int Salsa20_stream_destroy(void *salsaState);
int Salsa20_stream_encrypt(void *salsaState,
const uint8_t in[],
uint8_t out[], size_t len);
""")
class Salsa20Cipher:
"""Salsa20 cipher object. Do not create it directly. Use :py:func:`new`
instead.
:var nonce: The nonce with length 8
:vartype nonce: byte string
"""
def __init__(self, key, nonce):
"""Initialize a Salsa20 cipher object
See also `new()` at the module level."""
if len(key) not in key_size:
raise ValueError("Incorrect key length for Salsa20 (%d bytes)" % len(key))
if len(nonce) != 8:
raise ValueError("Incorrect nonce length for Salsa20 (%d bytes)" %
len(nonce))
self.nonce = _copy_bytes(None, None, nonce)
self._state = VoidPointer()
result = _raw_salsa20_lib.Salsa20_stream_init(
c_uint8_ptr(key),
c_size_t(len(key)),
c_uint8_ptr(nonce),
c_size_t(len(nonce)),
self._state.address_of())
if result:
raise ValueError("Error %d instantiating a Salsa20 cipher")
self._state = SmartPointer(self._state.get(),
_raw_salsa20_lib.Salsa20_stream_destroy)
self.block_size = 1
self.key_size = len(key)
def encrypt(self, plaintext, output=None):
"""Encrypt a piece of data.
Args:
plaintext(bytes/bytearray/memoryview): The data to encrypt, of any size.
Keyword Args:
output(bytes/bytearray/memoryview): The location where the ciphertext
is written to. If ``None``, the ciphertext is returned.
Returns:
If ``output`` is ``None``, the ciphertext is returned as ``bytes``.
Otherwise, ``None``.
"""
if output is None:
ciphertext = create_string_buffer(len(plaintext))
else:
ciphertext = output
if not is_writeable_buffer(output):
raise TypeError("output must be a bytearray or a writeable memoryview")
if len(plaintext) != len(output):
raise ValueError("output must have the same length as the input"
" (%d bytes)" % len(plaintext))
result = _raw_salsa20_lib.Salsa20_stream_encrypt(
self._state.get(),
c_uint8_ptr(plaintext),
c_uint8_ptr(ciphertext),
c_size_t(len(plaintext)))
if result:
raise ValueError("Error %d while encrypting with Salsa20" % result)
if output is None:
return get_raw_buffer(ciphertext)
else:
return None
def decrypt(self, ciphertext, output=None):
"""Decrypt a piece of data.
Args:
ciphertext(bytes/bytearray/memoryview): The data to decrypt, of any size.
Keyword Args:
output(bytes/bytearray/memoryview): The location where the plaintext
is written to. If ``None``, the plaintext is returned.
Returns:
If ``output`` is ``None``, the plaintext is returned as ``bytes``.
Otherwise, ``None``.
"""
try:
return self.encrypt(ciphertext, output=output)
except ValueError as e:
raise ValueError(str(e).replace("enc", "dec"))
def new(key, nonce=None):
"""Create a new Salsa20 cipher
:keyword key: The secret key to use. It must be 16 or 32 bytes long.
:type key: bytes/bytearray/memoryview
:keyword nonce:
A value that must never be reused for any other encryption
done with this key. It must be 8 bytes long.
If not provided, a random byte string will be generated (you can read
it back via the ``nonce`` attribute of the returned object).
:type nonce: bytes/bytearray/memoryview
:Return: a :class:`Crypto.Cipher.Salsa20.Salsa20Cipher` object
"""
if nonce is None:
nonce = get_random_bytes(8)
return Salsa20Cipher(key, nonce)
# Size of a data block (in bytes)
block_size = 1
# Size of a key (in bytes)
key_size = (16, 32)

View File

@ -0,0 +1,131 @@
# ===================================================================
#
# Copyright (c) 2019, Legrandin <helderijs@gmail.com>
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
#
# 1. Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# 2. Redistributions in binary form must reproduce the above copyright
# notice, this list of conditions and the following disclaimer in
# the documentation and/or other materials provided with the
# distribution.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
# COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.
# ===================================================================
import sys
from Crypto.Cipher import _create_cipher
from Crypto.Util._raw_api import (load_pycryptodome_raw_lib,
VoidPointer, SmartPointer, c_size_t,
c_uint8_ptr, c_uint)
_raw_blowfish_lib = load_pycryptodome_raw_lib(
"Crypto.Cipher._raw_eksblowfish",
"""
int EKSBlowfish_start_operation(const uint8_t key[],
size_t key_len,
const uint8_t salt[16],
size_t salt_len,
unsigned cost,
unsigned invert,
void **pResult);
int EKSBlowfish_encrypt(const void *state,
const uint8_t *in,
uint8_t *out,
size_t data_len);
int EKSBlowfish_decrypt(const void *state,
const uint8_t *in,
uint8_t *out,
size_t data_len);
int EKSBlowfish_stop_operation(void *state);
"""
)
def _create_base_cipher(dict_parameters):
"""This method instantiates and returns a smart pointer to
a low-level base cipher. It will absorb named parameters in
the process."""
try:
key = dict_parameters.pop("key")
salt = dict_parameters.pop("salt")
cost = dict_parameters.pop("cost")
except KeyError as e:
raise TypeError("Missing EKSBlowfish parameter: " + str(e))
invert = dict_parameters.pop("invert", True)
if len(key) not in key_size:
raise ValueError("Incorrect EKSBlowfish key length (%d bytes)" % len(key))
start_operation = _raw_blowfish_lib.EKSBlowfish_start_operation
stop_operation = _raw_blowfish_lib.EKSBlowfish_stop_operation
void_p = VoidPointer()
result = start_operation(c_uint8_ptr(key),
c_size_t(len(key)),
c_uint8_ptr(salt),
c_size_t(len(salt)),
c_uint(cost),
c_uint(int(invert)),
void_p.address_of())
if result:
raise ValueError("Error %X while instantiating the EKSBlowfish cipher"
% result)
return SmartPointer(void_p.get(), stop_operation)
def new(key, mode, salt, cost, invert):
"""Create a new EKSBlowfish cipher
Args:
key (bytes, bytearray, memoryview):
The secret key to use in the symmetric cipher.
Its length can vary from 0 to 72 bytes.
mode (one of the supported ``MODE_*`` constants):
The chaining mode to use for encryption or decryption.
salt (bytes, bytearray, memoryview):
The salt that bcrypt uses to thwart rainbow table attacks
cost (integer):
The complexity factor in bcrypt
invert (bool):
If ``False``, in the inner loop use ``ExpandKey`` first over the salt
and then over the key, as defined in
the `original bcrypt specification <https://www.usenix.org/legacy/events/usenix99/provos/provos_html/node4.html>`_.
If ``True``, reverse the order, as in the first implementation of
`bcrypt` in OpenBSD.
:Return: an EKSBlowfish object
"""
kwargs = { 'salt':salt, 'cost':cost, 'invert':invert }
return _create_cipher(sys.modules[__name__], key, mode, **kwargs)
MODE_ECB = 1
# Size of a data block (in bytes)
block_size = 8
# Size of a key (in bytes)
key_size = range(0, 72 + 1)

View File

@ -0,0 +1,79 @@
#
# A block cipher is instantiated as a combination of:
# 1. A base cipher (such as AES)
# 2. A mode of operation (such as CBC)
#
# Both items are implemented as C modules.
#
# The API of #1 is (replace "AES" with the name of the actual cipher):
# - AES_start_operaion(key) --> base_cipher_state
# - AES_encrypt(base_cipher_state, in, out, length)
# - AES_decrypt(base_cipher_state, in, out, length)
# - AES_stop_operation(base_cipher_state)
#
# Where base_cipher_state is AES_State, a struct with BlockBase (set of
# pointers to encrypt/decrypt/stop) followed by cipher-specific data.
#
# The API of #2 is (replace "CBC" with the name of the actual mode):
# - CBC_start_operation(base_cipher_state) --> mode_state
# - CBC_encrypt(mode_state, in, out, length)
# - CBC_decrypt(mode_state, in, out, length)
# - CBC_stop_operation(mode_state)
#
# where mode_state is a a pointer to base_cipher_state plus mode-specific data.
import os
from Crypto.Cipher._mode_ecb import _create_ecb_cipher
from Crypto.Cipher._mode_cbc import _create_cbc_cipher
from Crypto.Cipher._mode_cfb import _create_cfb_cipher
from Crypto.Cipher._mode_ofb import _create_ofb_cipher
from Crypto.Cipher._mode_ctr import _create_ctr_cipher
from Crypto.Cipher._mode_openpgp import _create_openpgp_cipher
from Crypto.Cipher._mode_ccm import _create_ccm_cipher
from Crypto.Cipher._mode_eax import _create_eax_cipher
from Crypto.Cipher._mode_siv import _create_siv_cipher
from Crypto.Cipher._mode_gcm import _create_gcm_cipher
from Crypto.Cipher._mode_ocb import _create_ocb_cipher
_modes = { 1:_create_ecb_cipher,
2:_create_cbc_cipher,
3:_create_cfb_cipher,
5:_create_ofb_cipher,
6:_create_ctr_cipher,
7:_create_openpgp_cipher,
9:_create_eax_cipher
}
_extra_modes = { 8:_create_ccm_cipher,
10:_create_siv_cipher,
11:_create_gcm_cipher,
12:_create_ocb_cipher
}
def _create_cipher(factory, key, mode, *args, **kwargs):
kwargs["key"] = key
modes = dict(_modes)
if kwargs.pop("add_aes_modes", False):
modes.update(_extra_modes)
if not mode in modes:
raise ValueError("Mode not supported")
if args:
if mode in (8, 9, 10, 11, 12):
if len(args) > 1:
raise TypeError("Too many arguments for this mode")
kwargs["nonce"] = args[0]
elif mode in (2, 3, 5, 7):
if len(args) > 1:
raise TypeError("Too many arguments for this mode")
kwargs["IV"] = args[0]
elif mode == 6:
if len(args) > 0:
raise TypeError("Too many arguments for this mode")
elif mode == 1:
raise TypeError("IV is not meaningful for the ECB mode")
return modes[mode](factory, **kwargs)

View File

@ -0,0 +1,293 @@
# ===================================================================
#
# Copyright (c) 2014, Legrandin <helderijs@gmail.com>
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
#
# 1. Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# 2. Redistributions in binary form must reproduce the above copyright
# notice, this list of conditions and the following disclaimer in
# the documentation and/or other materials provided with the
# distribution.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
# COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.
# ===================================================================
"""
Ciphertext Block Chaining (CBC) mode.
"""
__all__ = ['CbcMode']
from Crypto.Util.py3compat import _copy_bytes
from Crypto.Util._raw_api import (load_pycryptodome_raw_lib, VoidPointer,
create_string_buffer, get_raw_buffer,
SmartPointer, c_size_t, c_uint8_ptr,
is_writeable_buffer)
from Crypto.Random import get_random_bytes
raw_cbc_lib = load_pycryptodome_raw_lib("Crypto.Cipher._raw_cbc", """
int CBC_start_operation(void *cipher,
const uint8_t iv[],
size_t iv_len,
void **pResult);
int CBC_encrypt(void *cbcState,
const uint8_t *in,
uint8_t *out,
size_t data_len);
int CBC_decrypt(void *cbcState,
const uint8_t *in,
uint8_t *out,
size_t data_len);
int CBC_stop_operation(void *state);
"""
)
class CbcMode(object):
"""*Cipher-Block Chaining (CBC)*.
Each of the ciphertext blocks depends on the current
and all previous plaintext blocks.
An Initialization Vector (*IV*) is required.
See `NIST SP800-38A`_ , Section 6.2 .
.. _`NIST SP800-38A` : http://csrc.nist.gov/publications/nistpubs/800-38a/sp800-38a.pdf
:undocumented: __init__
"""
def __init__(self, block_cipher, iv):
"""Create a new block cipher, configured in CBC mode.
:Parameters:
block_cipher : C pointer
A smart pointer to the low-level block cipher instance.
iv : bytes/bytearray/memoryview
The initialization vector to use for encryption or decryption.
It is as long as the cipher block.
**The IV must be unpredictable**. Ideally it is picked randomly.
Reusing the *IV* for encryptions performed with the same key
compromises confidentiality.
"""
self._state = VoidPointer()
result = raw_cbc_lib.CBC_start_operation(block_cipher.get(),
c_uint8_ptr(iv),
c_size_t(len(iv)),
self._state.address_of())
if result:
raise ValueError("Error %d while instantiating the CBC mode"
% result)
# Ensure that object disposal of this Python object will (eventually)
# free the memory allocated by the raw library for the cipher mode
self._state = SmartPointer(self._state.get(),
raw_cbc_lib.CBC_stop_operation)
# Memory allocated for the underlying block cipher is now owed
# by the cipher mode
block_cipher.release()
self.block_size = len(iv)
"""The block size of the underlying cipher, in bytes."""
self.iv = _copy_bytes(None, None, iv)
"""The Initialization Vector originally used to create the object.
The value does not change."""
self.IV = self.iv
"""Alias for `iv`"""
self._next = [ self.encrypt, self.decrypt ]
def encrypt(self, plaintext, output=None):
"""Encrypt data with the key and the parameters set at initialization.
A cipher object is stateful: once you have encrypted a message
you cannot encrypt (or decrypt) another message using the same
object.
The data to encrypt can be broken up in two or
more pieces and `encrypt` can be called multiple times.
That is, the statement:
>>> c.encrypt(a) + c.encrypt(b)
is equivalent to:
>>> c.encrypt(a+b)
That also means that you cannot reuse an object for encrypting
or decrypting other data with the same key.
This function does not add any padding to the plaintext.
:Parameters:
plaintext : bytes/bytearray/memoryview
The piece of data to encrypt.
Its lenght must be multiple of the cipher block size.
:Keywords:
output : bytearray/memoryview
The location where the ciphertext must be written to.
If ``None``, the ciphertext is returned.
:Return:
If ``output`` is ``None``, the ciphertext is returned as ``bytes``.
Otherwise, ``None``.
"""
if self.encrypt not in self._next:
raise TypeError("encrypt() cannot be called after decrypt()")
self._next = [ self.encrypt ]
if output is None:
ciphertext = create_string_buffer(len(plaintext))
else:
ciphertext = output
if not is_writeable_buffer(output):
raise TypeError("output must be a bytearray or a writeable memoryview")
if len(plaintext) != len(output):
raise ValueError("output must have the same length as the input"
" (%d bytes)" % len(plaintext))
result = raw_cbc_lib.CBC_encrypt(self._state.get(),
c_uint8_ptr(plaintext),
c_uint8_ptr(ciphertext),
c_size_t(len(plaintext)))
if result:
if result == 3:
raise ValueError("Data must be padded to %d byte boundary in CBC mode" % self.block_size)
raise ValueError("Error %d while encrypting in CBC mode" % result)
if output is None:
return get_raw_buffer(ciphertext)
else:
return None
def decrypt(self, ciphertext, output=None):
"""Decrypt data with the key and the parameters set at initialization.
A cipher object is stateful: once you have decrypted a message
you cannot decrypt (or encrypt) another message with the same
object.
The data to decrypt can be broken up in two or
more pieces and `decrypt` can be called multiple times.
That is, the statement:
>>> c.decrypt(a) + c.decrypt(b)
is equivalent to:
>>> c.decrypt(a+b)
This function does not remove any padding from the plaintext.
:Parameters:
ciphertext : bytes/bytearray/memoryview
The piece of data to decrypt.
Its length must be multiple of the cipher block size.
:Keywords:
output : bytearray/memoryview
The location where the plaintext must be written to.
If ``None``, the plaintext is returned.
:Return:
If ``output`` is ``None``, the plaintext is returned as ``bytes``.
Otherwise, ``None``.
"""
if self.decrypt not in self._next:
raise TypeError("decrypt() cannot be called after encrypt()")
self._next = [ self.decrypt ]
if output is None:
plaintext = create_string_buffer(len(ciphertext))
else:
plaintext = output
if not is_writeable_buffer(output):
raise TypeError("output must be a bytearray or a writeable memoryview")
if len(ciphertext) != len(output):
raise ValueError("output must have the same length as the input"
" (%d bytes)" % len(plaintext))
result = raw_cbc_lib.CBC_decrypt(self._state.get(),
c_uint8_ptr(ciphertext),
c_uint8_ptr(plaintext),
c_size_t(len(ciphertext)))
if result:
if result == 3:
raise ValueError("Data must be padded to %d byte boundary in CBC mode" % self.block_size)
raise ValueError("Error %d while decrypting in CBC mode" % result)
if output is None:
return get_raw_buffer(plaintext)
else:
return None
def _create_cbc_cipher(factory, **kwargs):
"""Instantiate a cipher object that performs CBC encryption/decryption.
:Parameters:
factory : module
The underlying block cipher, a module from ``Crypto.Cipher``.
:Keywords:
iv : bytes/bytearray/memoryview
The IV to use for CBC.
IV : bytes/bytearray/memoryview
Alias for ``iv``.
Any other keyword will be passed to the underlying block cipher.
See the relevant documentation for details (at least ``key`` will need
to be present).
"""
cipher_state = factory._create_base_cipher(kwargs)
iv = kwargs.pop("IV", None)
IV = kwargs.pop("iv", None)
if (None, None) == (iv, IV):
iv = get_random_bytes(factory.block_size)
if iv is not None:
if IV is not None:
raise TypeError("You must either use 'iv' or 'IV', not both")
else:
iv = IV
if len(iv) != factory.block_size:
raise ValueError("Incorrect IV length (it must be %d bytes long)" %
factory.block_size)
if kwargs:
raise TypeError("Unknown parameters for CBC: %s" % str(kwargs))
return CbcMode(cipher_state, iv)

View File

@ -0,0 +1,650 @@
# ===================================================================
#
# Copyright (c) 2014, Legrandin <helderijs@gmail.com>
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
#
# 1. Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# 2. Redistributions in binary form must reproduce the above copyright
# notice, this list of conditions and the following disclaimer in
# the documentation and/or other materials provided with the
# distribution.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
# COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.
# ===================================================================
"""
Counter with CBC-MAC (CCM) mode.
"""
__all__ = ['CcmMode']
import struct
from binascii import unhexlify
from Crypto.Util.py3compat import (byte_string, bord,
_copy_bytes)
from Crypto.Util._raw_api import is_writeable_buffer
from Crypto.Util.strxor import strxor
from Crypto.Util.number import long_to_bytes
from Crypto.Hash import BLAKE2s
from Crypto.Random import get_random_bytes
def enum(**enums):
return type('Enum', (), enums)
MacStatus = enum(NOT_STARTED=0, PROCESSING_AUTH_DATA=1, PROCESSING_PLAINTEXT=2)
class CcmMode(object):
"""Counter with CBC-MAC (CCM).
This is an Authenticated Encryption with Associated Data (`AEAD`_) mode.
It provides both confidentiality and authenticity.
The header of the message may be left in the clear, if needed, and it will
still be subject to authentication. The decryption step tells the receiver
if the message comes from a source that really knowns the secret key.
Additionally, decryption detects if any part of the message - including the
header - has been modified or corrupted.
This mode requires a nonce. The nonce shall never repeat for two
different messages encrypted with the same key, but it does not need
to be random.
Note that there is a trade-off between the size of the nonce and the
maximum size of a single message you can encrypt.
It is important to use a large nonce if the key is reused across several
messages and the nonce is chosen randomly.
It is acceptable to us a short nonce if the key is only used a few times or
if the nonce is taken from a counter.
The following table shows the trade-off when the nonce is chosen at
random. The column on the left shows how many messages it takes
for the keystream to repeat **on average**. In practice, you will want to
stop using the key way before that.
+--------------------+---------------+-------------------+
| Avg. # of messages | nonce | Max. message |
| before keystream | size | size |
| repeats | (bytes) | (bytes) |
+====================+===============+===================+
| 2^52 | 13 | 64K |
+--------------------+---------------+-------------------+
| 2^48 | 12 | 16M |
+--------------------+---------------+-------------------+
| 2^44 | 11 | 4G |
+--------------------+---------------+-------------------+
| 2^40 | 10 | 1T |
+--------------------+---------------+-------------------+
| 2^36 | 9 | 64P |
+--------------------+---------------+-------------------+
| 2^32 | 8 | 16E |
+--------------------+---------------+-------------------+
This mode is only available for ciphers that operate on 128 bits blocks
(e.g. AES but not TDES).
See `NIST SP800-38C`_ or RFC3610_.
.. _`NIST SP800-38C`: http://csrc.nist.gov/publications/nistpubs/800-38C/SP800-38C.pdf
.. _RFC3610: https://tools.ietf.org/html/rfc3610
.. _AEAD: http://blog.cryptographyengineering.com/2012/05/how-to-choose-authenticated-encryption.html
:undocumented: __init__
"""
def __init__(self, factory, key, nonce, mac_len, msg_len, assoc_len,
cipher_params):
self.block_size = factory.block_size
"""The block size of the underlying cipher, in bytes."""
self.nonce = _copy_bytes(None, None, nonce)
"""The nonce used for this cipher instance"""
self._factory = factory
self._key = _copy_bytes(None, None, key)
self._mac_len = mac_len
self._msg_len = msg_len
self._assoc_len = assoc_len
self._cipher_params = cipher_params
self._mac_tag = None # Cache for MAC tag
if self.block_size != 16:
raise ValueError("CCM mode is only available for ciphers"
" that operate on 128 bits blocks")
# MAC tag length (Tlen)
if mac_len not in (4, 6, 8, 10, 12, 14, 16):
raise ValueError("Parameter 'mac_len' must be even"
" and in the range 4..16 (not %d)" % mac_len)
# Nonce value
if not (nonce and 7 <= len(nonce) <= 13):
raise ValueError("Length of parameter 'nonce' must be"
" in the range 7..13 bytes")
# Create MAC object (the tag will be the last block
# bytes worth of ciphertext)
self._mac = self._factory.new(key,
factory.MODE_CBC,
iv=b'\x00' * 16,
**cipher_params)
self._mac_status = MacStatus.NOT_STARTED
self._t = None
# Allowed transitions after initialization
self._next = [self.update, self.encrypt, self.decrypt,
self.digest, self.verify]
# Cumulative lengths
self._cumul_assoc_len = 0
self._cumul_msg_len = 0
# Cache for unaligned associated data/plaintext.
# This is a list with byte strings, but when the MAC starts,
# it will become a binary string no longer than the block size.
self._cache = []
# Start CTR cipher, by formatting the counter (A.3)
q = 15 - len(nonce) # length of Q, the encoded message length
self._cipher = self._factory.new(key,
self._factory.MODE_CTR,
nonce=struct.pack("B", q - 1) + self.nonce,
**cipher_params)
# S_0, step 6 in 6.1 for j=0
self._s_0 = self._cipher.encrypt(b'\x00' * 16)
# Try to start the MAC
if None not in (assoc_len, msg_len):
self._start_mac()
def _start_mac(self):
assert(self._mac_status == MacStatus.NOT_STARTED)
assert(None not in (self._assoc_len, self._msg_len))
assert(isinstance(self._cache, list))
# Formatting control information and nonce (A.2.1)
q = 15 - len(self.nonce) # length of Q, the encoded message length
flags = (64 * (self._assoc_len > 0) + 8 * ((self._mac_len - 2) // 2) +
(q - 1))
b_0 = struct.pack("B", flags) + self.nonce + long_to_bytes(self._msg_len, q)
# Formatting associated data (A.2.2)
# Encoded 'a' is concatenated with the associated data 'A'
assoc_len_encoded = b''
if self._assoc_len > 0:
if self._assoc_len < (2 ** 16 - 2 ** 8):
enc_size = 2
elif self._assoc_len < (2 ** 32):
assoc_len_encoded = b'\xFF\xFE'
enc_size = 4
else:
assoc_len_encoded = b'\xFF\xFF'
enc_size = 8
assoc_len_encoded += long_to_bytes(self._assoc_len, enc_size)
# b_0 and assoc_len_encoded must be processed first
self._cache.insert(0, b_0)
self._cache.insert(1, assoc_len_encoded)
# Process all the data cached so far
first_data_to_mac = b"".join(self._cache)
self._cache = b""
self._mac_status = MacStatus.PROCESSING_AUTH_DATA
self._update(first_data_to_mac)
def _pad_cache_and_update(self):
assert(self._mac_status != MacStatus.NOT_STARTED)
assert(len(self._cache) < self.block_size)
# Associated data is concatenated with the least number
# of zero bytes (possibly none) to reach alignment to
# the 16 byte boundary (A.2.3)
len_cache = len(self._cache)
if len_cache > 0:
self._update(b'\x00' * (self.block_size - len_cache))
def update(self, assoc_data):
"""Protect associated data
If there is any associated data, the caller has to invoke
this function one or more times, before using
``decrypt`` or ``encrypt``.
By *associated data* it is meant any data (e.g. packet headers) that
will not be encrypted and will be transmitted in the clear.
However, the receiver is still able to detect any modification to it.
In CCM, the *associated data* is also called
*additional authenticated data* (AAD).
If there is no associated data, this method must not be called.
The caller may split associated data in segments of any size, and
invoke this method multiple times, each time with the next segment.
:Parameters:
assoc_data : bytes/bytearray/memoryview
A piece of associated data. There are no restrictions on its size.
"""
if self.update not in self._next:
raise TypeError("update() can only be called"
" immediately after initialization")
self._next = [self.update, self.encrypt, self.decrypt,
self.digest, self.verify]
self._cumul_assoc_len += len(assoc_data)
if self._assoc_len is not None and \
self._cumul_assoc_len > self._assoc_len:
raise ValueError("Associated data is too long")
self._update(assoc_data)
return self
def _update(self, assoc_data_pt=b""):
"""Update the MAC with associated data or plaintext
(without FSM checks)"""
# If MAC has not started yet, we just park the data into a list.
# If the data is mutable, we create a copy and store that instead.
if self._mac_status == MacStatus.NOT_STARTED:
if is_writeable_buffer(assoc_data_pt):
assoc_data_pt = _copy_bytes(None, None, assoc_data_pt)
self._cache.append(assoc_data_pt)
return
assert(len(self._cache) < self.block_size)
if len(self._cache) > 0:
filler = min(self.block_size - len(self._cache),
len(assoc_data_pt))
self._cache += _copy_bytes(None, filler, assoc_data_pt)
assoc_data_pt = _copy_bytes(filler, None, assoc_data_pt)
if len(self._cache) < self.block_size:
return
# The cache is exactly one block
self._t = self._mac.encrypt(self._cache)
self._cache = b""
update_len = len(assoc_data_pt) // self.block_size * self.block_size
self._cache = _copy_bytes(update_len, None, assoc_data_pt)
if update_len > 0:
self._t = self._mac.encrypt(assoc_data_pt[:update_len])[-16:]
def encrypt(self, plaintext, output=None):
"""Encrypt data with the key set at initialization.
A cipher object is stateful: once you have encrypted a message
you cannot encrypt (or decrypt) another message using the same
object.
This method can be called only **once** if ``msg_len`` was
not passed at initialization.
If ``msg_len`` was given, the data to encrypt can be broken
up in two or more pieces and `encrypt` can be called
multiple times.
That is, the statement:
>>> c.encrypt(a) + c.encrypt(b)
is equivalent to:
>>> c.encrypt(a+b)
This function does not add any padding to the plaintext.
:Parameters:
plaintext : bytes/bytearray/memoryview
The piece of data to encrypt.
It can be of any length.
:Keywords:
output : bytearray/memoryview
The location where the ciphertext must be written to.
If ``None``, the ciphertext is returned.
:Return:
If ``output`` is ``None``, the ciphertext as ``bytes``.
Otherwise, ``None``.
"""
if self.encrypt not in self._next:
raise TypeError("encrypt() can only be called after"
" initialization or an update()")
self._next = [self.encrypt, self.digest]
# No more associated data allowed from now
if self._assoc_len is None:
assert(isinstance(self._cache, list))
self._assoc_len = sum([len(x) for x in self._cache])
if self._msg_len is not None:
self._start_mac()
else:
if self._cumul_assoc_len < self._assoc_len:
raise ValueError("Associated data is too short")
# Only once piece of plaintext accepted if message length was
# not declared in advance
if self._msg_len is None:
self._msg_len = len(plaintext)
self._start_mac()
self._next = [self.digest]
self._cumul_msg_len += len(plaintext)
if self._cumul_msg_len > self._msg_len:
raise ValueError("Message is too long")
if self._mac_status == MacStatus.PROCESSING_AUTH_DATA:
# Associated data is concatenated with the least number
# of zero bytes (possibly none) to reach alignment to
# the 16 byte boundary (A.2.3)
self._pad_cache_and_update()
self._mac_status = MacStatus.PROCESSING_PLAINTEXT
self._update(plaintext)
return self._cipher.encrypt(plaintext, output=output)
def decrypt(self, ciphertext, output=None):
"""Decrypt data with the key set at initialization.
A cipher object is stateful: once you have decrypted a message
you cannot decrypt (or encrypt) another message with the same
object.
This method can be called only **once** if ``msg_len`` was
not passed at initialization.
If ``msg_len`` was given, the data to decrypt can be
broken up in two or more pieces and `decrypt` can be
called multiple times.
That is, the statement:
>>> c.decrypt(a) + c.decrypt(b)
is equivalent to:
>>> c.decrypt(a+b)
This function does not remove any padding from the plaintext.
:Parameters:
ciphertext : bytes/bytearray/memoryview
The piece of data to decrypt.
It can be of any length.
:Keywords:
output : bytearray/memoryview
The location where the plaintext must be written to.
If ``None``, the plaintext is returned.
:Return:
If ``output`` is ``None``, the plaintext as ``bytes``.
Otherwise, ``None``.
"""
if self.decrypt not in self._next:
raise TypeError("decrypt() can only be called"
" after initialization or an update()")
self._next = [self.decrypt, self.verify]
# No more associated data allowed from now
if self._assoc_len is None:
assert(isinstance(self._cache, list))
self._assoc_len = sum([len(x) for x in self._cache])
if self._msg_len is not None:
self._start_mac()
else:
if self._cumul_assoc_len < self._assoc_len:
raise ValueError("Associated data is too short")
# Only once piece of ciphertext accepted if message length was
# not declared in advance
if self._msg_len is None:
self._msg_len = len(ciphertext)
self._start_mac()
self._next = [self.verify]
self._cumul_msg_len += len(ciphertext)
if self._cumul_msg_len > self._msg_len:
raise ValueError("Message is too long")
if self._mac_status == MacStatus.PROCESSING_AUTH_DATA:
# Associated data is concatenated with the least number
# of zero bytes (possibly none) to reach alignment to
# the 16 byte boundary (A.2.3)
self._pad_cache_and_update()
self._mac_status = MacStatus.PROCESSING_PLAINTEXT
# Encrypt is equivalent to decrypt with the CTR mode
plaintext = self._cipher.encrypt(ciphertext, output=output)
if output is None:
self._update(plaintext)
else:
self._update(output)
return plaintext
def digest(self):
"""Compute the *binary* MAC tag.
The caller invokes this function at the very end.
This method returns the MAC that shall be sent to the receiver,
together with the ciphertext.
:Return: the MAC, as a byte string.
"""
if self.digest not in self._next:
raise TypeError("digest() cannot be called when decrypting"
" or validating a message")
self._next = [self.digest]
return self._digest()
def _digest(self):
if self._mac_tag:
return self._mac_tag
if self._assoc_len is None:
assert(isinstance(self._cache, list))
self._assoc_len = sum([len(x) for x in self._cache])
if self._msg_len is not None:
self._start_mac()
else:
if self._cumul_assoc_len < self._assoc_len:
raise ValueError("Associated data is too short")
if self._msg_len is None:
self._msg_len = 0
self._start_mac()
if self._cumul_msg_len != self._msg_len:
raise ValueError("Message is too short")
# Both associated data and payload are concatenated with the least
# number of zero bytes (possibly none) that align it to the
# 16 byte boundary (A.2.2 and A.2.3)
self._pad_cache_and_update()
# Step 8 in 6.1 (T xor MSB_Tlen(S_0))
self._mac_tag = strxor(self._t, self._s_0)[:self._mac_len]
return self._mac_tag
def hexdigest(self):
"""Compute the *printable* MAC tag.
This method is like `digest`.
:Return: the MAC, as a hexadecimal string.
"""
return "".join(["%02x" % bord(x) for x in self.digest()])
def verify(self, received_mac_tag):
"""Validate the *binary* MAC tag.
The caller invokes this function at the very end.
This method checks if the decrypted message is indeed valid
(that is, if the key is correct) and it has not been
tampered with while in transit.
:Parameters:
received_mac_tag : bytes/bytearray/memoryview
This is the *binary* MAC, as received from the sender.
:Raises ValueError:
if the MAC does not match. The message has been tampered with
or the key is incorrect.
"""
if self.verify not in self._next:
raise TypeError("verify() cannot be called"
" when encrypting a message")
self._next = [self.verify]
self._digest()
secret = get_random_bytes(16)
mac1 = BLAKE2s.new(digest_bits=160, key=secret, data=self._mac_tag)
mac2 = BLAKE2s.new(digest_bits=160, key=secret, data=received_mac_tag)
if mac1.digest() != mac2.digest():
raise ValueError("MAC check failed")
def hexverify(self, hex_mac_tag):
"""Validate the *printable* MAC tag.
This method is like `verify`.
:Parameters:
hex_mac_tag : string
This is the *printable* MAC, as received from the sender.
:Raises ValueError:
if the MAC does not match. The message has been tampered with
or the key is incorrect.
"""
self.verify(unhexlify(hex_mac_tag))
def encrypt_and_digest(self, plaintext, output=None):
"""Perform encrypt() and digest() in one step.
:Parameters:
plaintext : bytes/bytearray/memoryview
The piece of data to encrypt.
:Keywords:
output : bytearray/memoryview
The location where the ciphertext must be written to.
If ``None``, the ciphertext is returned.
:Return:
a tuple with two items:
- the ciphertext, as ``bytes``
- the MAC tag, as ``bytes``
The first item becomes ``None`` when the ``output`` parameter
specified a location for the result.
"""
return self.encrypt(plaintext, output=output), self.digest()
def decrypt_and_verify(self, ciphertext, received_mac_tag, output=None):
"""Perform decrypt() and verify() in one step.
:Parameters:
ciphertext : bytes/bytearray/memoryview
The piece of data to decrypt.
received_mac_tag : bytes/bytearray/memoryview
This is the *binary* MAC, as received from the sender.
:Keywords:
output : bytearray/memoryview
The location where the plaintext must be written to.
If ``None``, the plaintext is returned.
:Return: the plaintext as ``bytes`` or ``None`` when the ``output``
parameter specified a location for the result.
:Raises ValueError:
if the MAC does not match. The message has been tampered with
or the key is incorrect.
"""
plaintext = self.decrypt(ciphertext, output=output)
self.verify(received_mac_tag)
return plaintext
def _create_ccm_cipher(factory, **kwargs):
"""Create a new block cipher, configured in CCM mode.
:Parameters:
factory : module
A symmetric cipher module from `Crypto.Cipher` (like
`Crypto.Cipher.AES`).
:Keywords:
key : bytes/bytearray/memoryview
The secret key to use in the symmetric cipher.
nonce : bytes/bytearray/memoryview
A value that must never be reused for any other encryption.
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.
If not specified, a 11 byte long random string is used.
mac_len : integer
Length of the MAC, in bytes. It must be even and in
the range ``[4..16]``. The default is 16.
msg_len : integer
Length of the message to (de)cipher.
If not specified, ``encrypt`` or ``decrypt`` may only be called once.
assoc_len : integer
Length of the associated data.
If not specified, all data is internally buffered.
"""
try:
key = key = kwargs.pop("key")
except KeyError as e:
raise TypeError("Missing parameter: " + str(e))
nonce = kwargs.pop("nonce", None) # N
if nonce is None:
nonce = get_random_bytes(11)
mac_len = kwargs.pop("mac_len", factory.block_size)
msg_len = kwargs.pop("msg_len", None) # p
assoc_len = kwargs.pop("assoc_len", None) # a
cipher_params = dict(kwargs)
return CcmMode(factory, key, nonce, mac_len, msg_len,
assoc_len, cipher_params)

View File

@ -0,0 +1,293 @@
# -*- coding: utf-8 -*-
#
# Cipher/mode_cfb.py : CFB mode
#
# ===================================================================
# 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.
# ===================================================================
"""
Counter Feedback (CFB) mode.
"""
__all__ = ['CfbMode']
from Crypto.Util.py3compat import _copy_bytes
from Crypto.Util._raw_api import (load_pycryptodome_raw_lib, VoidPointer,
create_string_buffer, get_raw_buffer,
SmartPointer, c_size_t, c_uint8_ptr,
is_writeable_buffer)
from Crypto.Random import get_random_bytes
raw_cfb_lib = load_pycryptodome_raw_lib("Crypto.Cipher._raw_cfb","""
int CFB_start_operation(void *cipher,
const uint8_t iv[],
size_t iv_len,
size_t segment_len, /* In bytes */
void **pResult);
int CFB_encrypt(void *cfbState,
const uint8_t *in,
uint8_t *out,
size_t data_len);
int CFB_decrypt(void *cfbState,
const uint8_t *in,
uint8_t *out,
size_t data_len);
int CFB_stop_operation(void *state);"""
)
class CfbMode(object):
"""*Cipher FeedBack (CFB)*.
This mode is similar to CFB, but it transforms
the underlying block cipher into a stream cipher.
Plaintext and ciphertext are processed in *segments*
of **s** bits. The mode is therefore sometimes
labelled **s**-bit CFB.
An Initialization Vector (*IV*) is required.
See `NIST SP800-38A`_ , Section 6.3.
.. _`NIST SP800-38A` : http://csrc.nist.gov/publications/nistpubs/800-38a/sp800-38a.pdf
:undocumented: __init__
"""
def __init__(self, block_cipher, iv, segment_size):
"""Create a new block cipher, configured in CFB mode.
:Parameters:
block_cipher : C pointer
A smart pointer to the low-level block cipher instance.
iv : bytes/bytearray/memoryview
The initialization vector to use for encryption or decryption.
It is as long as the cipher block.
**The IV must be unpredictable**. Ideally it is picked randomly.
Reusing the *IV* for encryptions performed with the same key
compromises confidentiality.
segment_size : integer
The number of bytes the plaintext and ciphertext are segmented in.
"""
self._state = VoidPointer()
result = raw_cfb_lib.CFB_start_operation(block_cipher.get(),
c_uint8_ptr(iv),
c_size_t(len(iv)),
c_size_t(segment_size),
self._state.address_of())
if result:
raise ValueError("Error %d while instantiating the CFB mode" % result)
# Ensure that object disposal of this Python object will (eventually)
# free the memory allocated by the raw library for the cipher mode
self._state = SmartPointer(self._state.get(),
raw_cfb_lib.CFB_stop_operation)
# Memory allocated for the underlying block cipher is now owed
# by the cipher mode
block_cipher.release()
self.block_size = len(iv)
"""The block size of the underlying cipher, in bytes."""
self.iv = _copy_bytes(None, None, iv)
"""The Initialization Vector originally used to create the object.
The value does not change."""
self.IV = self.iv
"""Alias for `iv`"""
self._next = [ self.encrypt, self.decrypt ]
def encrypt(self, plaintext, output=None):
"""Encrypt data with the key and the parameters set at initialization.
A cipher object is stateful: once you have encrypted a message
you cannot encrypt (or decrypt) another message using the same
object.
The data to encrypt can be broken up in two or
more pieces and `encrypt` can be called multiple times.
That is, the statement:
>>> c.encrypt(a) + c.encrypt(b)
is equivalent to:
>>> c.encrypt(a+b)
This function does not add any padding to the plaintext.
:Parameters:
plaintext : bytes/bytearray/memoryview
The piece of data to encrypt.
It can be of any length.
:Keywords:
output : bytearray/memoryview
The location where the ciphertext must be written to.
If ``None``, the ciphertext is returned.
:Return:
If ``output`` is ``None``, the ciphertext is returned as ``bytes``.
Otherwise, ``None``.
"""
if self.encrypt not in self._next:
raise TypeError("encrypt() cannot be called after decrypt()")
self._next = [ self.encrypt ]
if output is None:
ciphertext = create_string_buffer(len(plaintext))
else:
ciphertext = output
if not is_writeable_buffer(output):
raise TypeError("output must be a bytearray or a writeable memoryview")
if len(plaintext) != len(output):
raise ValueError("output must have the same length as the input"
" (%d bytes)" % len(plaintext))
result = raw_cfb_lib.CFB_encrypt(self._state.get(),
c_uint8_ptr(plaintext),
c_uint8_ptr(ciphertext),
c_size_t(len(plaintext)))
if result:
raise ValueError("Error %d while encrypting in CFB mode" % result)
if output is None:
return get_raw_buffer(ciphertext)
else:
return None
def decrypt(self, ciphertext, output=None):
"""Decrypt data with the key and the parameters set at initialization.
A cipher object is stateful: once you have decrypted a message
you cannot decrypt (or encrypt) another message with the same
object.
The data to decrypt can be broken up in two or
more pieces and `decrypt` can be called multiple times.
That is, the statement:
>>> c.decrypt(a) + c.decrypt(b)
is equivalent to:
>>> c.decrypt(a+b)
This function does not remove any padding from the plaintext.
:Parameters:
ciphertext : bytes/bytearray/memoryview
The piece of data to decrypt.
It can be of any length.
:Keywords:
output : bytearray/memoryview
The location where the plaintext must be written to.
If ``None``, the plaintext is returned.
:Return:
If ``output`` is ``None``, the plaintext is returned as ``bytes``.
Otherwise, ``None``.
"""
if self.decrypt not in self._next:
raise TypeError("decrypt() cannot be called after encrypt()")
self._next = [ self.decrypt ]
if output is None:
plaintext = create_string_buffer(len(ciphertext))
else:
plaintext = output
if not is_writeable_buffer(output):
raise TypeError("output must be a bytearray or a writeable memoryview")
if len(ciphertext) != len(output):
raise ValueError("output must have the same length as the input"
" (%d bytes)" % len(plaintext))
result = raw_cfb_lib.CFB_decrypt(self._state.get(),
c_uint8_ptr(ciphertext),
c_uint8_ptr(plaintext),
c_size_t(len(ciphertext)))
if result:
raise ValueError("Error %d while decrypting in CFB mode" % result)
if output is None:
return get_raw_buffer(plaintext)
else:
return None
def _create_cfb_cipher(factory, **kwargs):
"""Instantiate a cipher object that performs CFB encryption/decryption.
:Parameters:
factory : module
The underlying block cipher, a module from ``Crypto.Cipher``.
:Keywords:
iv : bytes/bytearray/memoryview
The IV to use for CFB.
IV : bytes/bytearray/memoryview
Alias for ``iv``.
segment_size : integer
The number of bit the plaintext and ciphertext are segmented in.
If not present, the default is 8.
Any other keyword will be passed to the underlying block cipher.
See the relevant documentation for details (at least ``key`` will need
to be present).
"""
cipher_state = factory._create_base_cipher(kwargs)
iv = kwargs.pop("IV", None)
IV = kwargs.pop("iv", None)
if (None, None) == (iv, IV):
iv = get_random_bytes(factory.block_size)
if iv is not None:
if IV is not None:
raise TypeError("You must either use 'iv' or 'IV', not both")
else:
iv = IV
if len(iv) != factory.block_size:
raise ValueError("Incorrect IV length (it must be %d bytes long)" %
factory.block_size)
segment_size_bytes, rem = divmod(kwargs.pop("segment_size", 8), 8)
if segment_size_bytes == 0 or rem != 0:
raise ValueError("'segment_size' must be positive and multiple of 8 bits")
if kwargs:
raise TypeError("Unknown parameters for CFB: %s" % str(kwargs))
return CfbMode(cipher_state, iv, segment_size_bytes)

View File

@ -0,0 +1,393 @@
# -*- coding: utf-8 -*-
#
# Cipher/mode_ctr.py : CTR mode
#
# ===================================================================
# 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.
# ===================================================================
"""
Counter (CTR) mode.
"""
__all__ = ['CtrMode']
import struct
from Crypto.Util._raw_api import (load_pycryptodome_raw_lib, VoidPointer,
create_string_buffer, get_raw_buffer,
SmartPointer, c_size_t, c_uint8_ptr,
is_writeable_buffer)
from Crypto.Random import get_random_bytes
from Crypto.Util.py3compat import _copy_bytes, is_native_int
from Crypto.Util.number import long_to_bytes
raw_ctr_lib = load_pycryptodome_raw_lib("Crypto.Cipher._raw_ctr", """
int CTR_start_operation(void *cipher,
uint8_t initialCounterBlock[],
size_t initialCounterBlock_len,
size_t prefix_len,
unsigned counter_len,
unsigned littleEndian,
void **pResult);
int CTR_encrypt(void *ctrState,
const uint8_t *in,
uint8_t *out,
size_t data_len);
int CTR_decrypt(void *ctrState,
const uint8_t *in,
uint8_t *out,
size_t data_len);
int CTR_stop_operation(void *ctrState);"""
)
class CtrMode(object):
"""*CounTeR (CTR)* mode.
This mode is very similar to ECB, in that
encryption of one block is done independently of all other blocks.
Unlike ECB, the block *position* contributes to the encryption
and no information leaks about symbol frequency.
Each message block is associated to a *counter* which
must be unique across all messages that get encrypted
with the same key (not just within the same message).
The counter is as big as the block size.
Counters can be generated in several ways. The most
straightword one is to choose an *initial counter block*
(which can be made public, similarly to the *IV* for the
other modes) and increment its lowest **m** bits by one
(modulo *2^m*) for each block. In most cases, **m** is
chosen to be half the block size.
See `NIST SP800-38A`_, Section 6.5 (for the mode) and
Appendix B (for how to manage the *initial counter block*).
.. _`NIST SP800-38A` : http://csrc.nist.gov/publications/nistpubs/800-38a/sp800-38a.pdf
:undocumented: __init__
"""
def __init__(self, block_cipher, initial_counter_block,
prefix_len, counter_len, little_endian):
"""Create a new block cipher, configured in CTR mode.
:Parameters:
block_cipher : C pointer
A smart pointer to the low-level block cipher instance.
initial_counter_block : bytes/bytearray/memoryview
The initial plaintext to use to generate the key stream.
It is as large as the cipher block, and it embeds
the initial value of the counter.
This value must not be reused.
It shall contain a nonce or a random component.
Reusing the *initial counter block* for encryptions
performed with the same key compromises confidentiality.
prefix_len : integer
The amount of bytes at the beginning of the counter block
that never change.
counter_len : integer
The length in bytes of the counter embedded in the counter
block.
little_endian : boolean
True if the counter in the counter block is an integer encoded
in little endian mode. If False, it is big endian.
"""
if len(initial_counter_block) == prefix_len + counter_len:
self.nonce = _copy_bytes(None, prefix_len, initial_counter_block)
"""Nonce; not available if there is a fixed suffix"""
self._state = VoidPointer()
result = raw_ctr_lib.CTR_start_operation(block_cipher.get(),
c_uint8_ptr(initial_counter_block),
c_size_t(len(initial_counter_block)),
c_size_t(prefix_len),
counter_len,
little_endian,
self._state.address_of())
if result:
raise ValueError("Error %X while instantiating the CTR mode"
% result)
# Ensure that object disposal of this Python object will (eventually)
# free the memory allocated by the raw library for the cipher mode
self._state = SmartPointer(self._state.get(),
raw_ctr_lib.CTR_stop_operation)
# Memory allocated for the underlying block cipher is now owed
# by the cipher mode
block_cipher.release()
self.block_size = len(initial_counter_block)
"""The block size of the underlying cipher, in bytes."""
self._next = [self.encrypt, self.decrypt]
def encrypt(self, plaintext, output=None):
"""Encrypt data with the key and the parameters set at initialization.
A cipher object is stateful: once you have encrypted a message
you cannot encrypt (or decrypt) another message using the same
object.
The data to encrypt can be broken up in two or
more pieces and `encrypt` can be called multiple times.
That is, the statement:
>>> c.encrypt(a) + c.encrypt(b)
is equivalent to:
>>> c.encrypt(a+b)
This function does not add any padding to the plaintext.
:Parameters:
plaintext : bytes/bytearray/memoryview
The piece of data to encrypt.
It can be of any length.
:Keywords:
output : bytearray/memoryview
The location where the ciphertext must be written to.
If ``None``, the ciphertext is returned.
:Return:
If ``output`` is ``None``, the ciphertext is returned as ``bytes``.
Otherwise, ``None``.
"""
if self.encrypt not in self._next:
raise TypeError("encrypt() cannot be called after decrypt()")
self._next = [self.encrypt]
if output is None:
ciphertext = create_string_buffer(len(plaintext))
else:
ciphertext = output
if not is_writeable_buffer(output):
raise TypeError("output must be a bytearray or a writeable memoryview")
if len(plaintext) != len(output):
raise ValueError("output must have the same length as the input"
" (%d bytes)" % len(plaintext))
result = raw_ctr_lib.CTR_encrypt(self._state.get(),
c_uint8_ptr(plaintext),
c_uint8_ptr(ciphertext),
c_size_t(len(plaintext)))
if result:
if result == 0x60002:
raise OverflowError("The counter has wrapped around in"
" CTR mode")
raise ValueError("Error %X while encrypting in CTR mode" % result)
if output is None:
return get_raw_buffer(ciphertext)
else:
return None
def decrypt(self, ciphertext, output=None):
"""Decrypt data with the key and the parameters set at initialization.
A cipher object is stateful: once you have decrypted a message
you cannot decrypt (or encrypt) another message with the same
object.
The data to decrypt can be broken up in two or
more pieces and `decrypt` can be called multiple times.
That is, the statement:
>>> c.decrypt(a) + c.decrypt(b)
is equivalent to:
>>> c.decrypt(a+b)
This function does not remove any padding from the plaintext.
:Parameters:
ciphertext : bytes/bytearray/memoryview
The piece of data to decrypt.
It can be of any length.
:Keywords:
output : bytearray/memoryview
The location where the plaintext must be written to.
If ``None``, the plaintext is returned.
:Return:
If ``output`` is ``None``, the plaintext is returned as ``bytes``.
Otherwise, ``None``.
"""
if self.decrypt not in self._next:
raise TypeError("decrypt() cannot be called after encrypt()")
self._next = [self.decrypt]
if output is None:
plaintext = create_string_buffer(len(ciphertext))
else:
plaintext = output
if not is_writeable_buffer(output):
raise TypeError("output must be a bytearray or a writeable memoryview")
if len(ciphertext) != len(output):
raise ValueError("output must have the same length as the input"
" (%d bytes)" % len(plaintext))
result = raw_ctr_lib.CTR_decrypt(self._state.get(),
c_uint8_ptr(ciphertext),
c_uint8_ptr(plaintext),
c_size_t(len(ciphertext)))
if result:
if result == 0x60002:
raise OverflowError("The counter has wrapped around in"
" CTR mode")
raise ValueError("Error %X while decrypting in CTR mode" % result)
if output is None:
return get_raw_buffer(plaintext)
else:
return None
def _create_ctr_cipher(factory, **kwargs):
"""Instantiate a cipher object that performs CTR encryption/decryption.
:Parameters:
factory : module
The underlying block cipher, a module from ``Crypto.Cipher``.
:Keywords:
nonce : bytes/bytearray/memoryview
The fixed part at the beginning of the counter block - the rest is
the counter number that gets increased when processing the next block.
The nonce must be such that no two messages are encrypted under the
same key and the same nonce.
The nonce must be shorter than the block size (it can have
zero length; the counter is then as long as the block).
If this parameter is not present, a random nonce will be created with
length equal to half the block size. No random nonce shorter than
64 bits will be created though - you must really think through all
security consequences of using such a short block size.
initial_value : posive integer or bytes/bytearray/memoryview
The initial value for the counter. If not present, the cipher will
start counting from 0. The value is incremented by one for each block.
The counter number is encoded in big endian mode.
counter : object
Instance of ``Crypto.Util.Counter``, which allows full customization
of the counter block. This parameter is incompatible to both ``nonce``
and ``initial_value``.
Any other keyword will be passed to the underlying block cipher.
See the relevant documentation for details (at least ``key`` will need
to be present).
"""
cipher_state = factory._create_base_cipher(kwargs)
counter = kwargs.pop("counter", None)
nonce = kwargs.pop("nonce", None)
initial_value = kwargs.pop("initial_value", None)
if kwargs:
raise TypeError("Invalid parameters for CTR mode: %s" % str(kwargs))
if counter is not None and (nonce, initial_value) != (None, None):
raise TypeError("'counter' and 'nonce'/'initial_value'"
" are mutually exclusive")
if counter is None:
# Crypto.Util.Counter is not used
if nonce is None:
if factory.block_size < 16:
raise TypeError("Impossible to create a safe nonce for short"
" block sizes")
nonce = get_random_bytes(factory.block_size // 2)
else:
if len(nonce) >= factory.block_size:
raise ValueError("Nonce is too long")
# What is not nonce is counter
counter_len = factory.block_size - len(nonce)
if initial_value is None:
initial_value = 0
if is_native_int(initial_value):
if (1 << (counter_len * 8)) - 1 < initial_value:
raise ValueError("Initial counter value is too large")
initial_counter_block = nonce + long_to_bytes(initial_value, counter_len)
else:
if len(initial_value) != counter_len:
raise ValueError("Incorrect length for counter byte string (%d bytes, expected %d)" %
(len(initial_value), counter_len))
initial_counter_block = nonce + initial_value
return CtrMode(cipher_state,
initial_counter_block,
len(nonce), # prefix
counter_len,
False) # little_endian
# Crypto.Util.Counter is used
# 'counter' used to be a callable object, but now it is
# just a dictionary for backward compatibility.
_counter = dict(counter)
try:
counter_len = _counter.pop("counter_len")
prefix = _counter.pop("prefix")
suffix = _counter.pop("suffix")
initial_value = _counter.pop("initial_value")
little_endian = _counter.pop("little_endian")
except KeyError:
raise TypeError("Incorrect counter object"
" (use Crypto.Util.Counter.new)")
# Compute initial counter block
words = []
while initial_value > 0:
words.append(struct.pack('B', initial_value & 255))
initial_value >>= 8
words += [b'\x00'] * max(0, counter_len - len(words))
if not little_endian:
words.reverse()
initial_counter_block = prefix + b"".join(words) + suffix
if len(initial_counter_block) != factory.block_size:
raise ValueError("Size of the counter block (%d bytes) must match"
" block size (%d)" % (len(initial_counter_block),
factory.block_size))
return CtrMode(cipher_state, initial_counter_block,
len(prefix), counter_len, little_endian)

View File

@ -0,0 +1,408 @@
# ===================================================================
#
# Copyright (c) 2014, Legrandin <helderijs@gmail.com>
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
#
# 1. Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# 2. Redistributions in binary form must reproduce the above copyright
# notice, this list of conditions and the following disclaimer in
# the documentation and/or other materials provided with the
# distribution.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
# COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.
# ===================================================================
"""
EAX mode.
"""
__all__ = ['EaxMode']
import struct
from binascii import unhexlify
from Crypto.Util.py3compat import byte_string, bord, _copy_bytes
from Crypto.Util._raw_api import is_buffer
from Crypto.Util.strxor import strxor
from Crypto.Util.number import long_to_bytes, bytes_to_long
from Crypto.Hash import CMAC, BLAKE2s
from Crypto.Random import get_random_bytes
class EaxMode(object):
"""*EAX* mode.
This is an Authenticated Encryption with Associated Data
(`AEAD`_) mode. It provides both confidentiality and authenticity.
The header of the message may be left in the clear, if needed,
and it will still be subject to authentication.
The decryption step tells the receiver if the message comes
from a source that really knowns the secret key.
Additionally, decryption detects if any part of the message -
including the header - has been modified or corrupted.
This mode requires a *nonce*.
This mode is only available for ciphers that operate on 64 or
128 bits blocks.
There are no official standards defining EAX.
The implementation is based on `a proposal`__ that
was presented to NIST.
.. _AEAD: http://blog.cryptographyengineering.com/2012/05/how-to-choose-authenticated-encryption.html
.. __: http://csrc.nist.gov/groups/ST/toolkit/BCM/documents/proposedmodes/eax/eax-spec.pdf
:undocumented: __init__
"""
def __init__(self, factory, key, nonce, mac_len, cipher_params):
"""EAX cipher mode"""
self.block_size = factory.block_size
"""The block size of the underlying cipher, in bytes."""
self.nonce = _copy_bytes(None, None, nonce)
"""The nonce originally used to create the object."""
self._mac_len = mac_len
self._mac_tag = None # Cache for MAC tag
# Allowed transitions after initialization
self._next = [self.update, self.encrypt, self.decrypt,
self.digest, self.verify]
# MAC tag length
if not (4 <= self._mac_len <= self.block_size):
raise ValueError("Parameter 'mac_len' must not be larger than %d"
% self.block_size)
# Nonce cannot be empty and must be a byte string
if len(self.nonce) == 0:
raise ValueError("Nonce cannot be empty in EAX mode")
if not is_buffer(nonce):
raise TypeError("nonce must be bytes, bytearray or memoryview")
self._omac = [
CMAC.new(key,
b'\x00' * (self.block_size - 1) + struct.pack('B', i),
ciphermod=factory,
cipher_params=cipher_params)
for i in range(0, 3)
]
# Compute MAC of nonce
self._omac[0].update(self.nonce)
self._signer = self._omac[1]
# MAC of the nonce is also the initial counter for CTR encryption
counter_int = bytes_to_long(self._omac[0].digest())
self._cipher = factory.new(key,
factory.MODE_CTR,
initial_value=counter_int,
nonce=b"",
**cipher_params)
def update(self, assoc_data):
"""Protect associated data
If there is any associated data, the caller has to invoke
this function one or more times, before using
``decrypt`` or ``encrypt``.
By *associated data* it is meant any data (e.g. packet headers) that
will not be encrypted and will be transmitted in the clear.
However, the receiver is still able to detect any modification to it.
If there is no associated data, this method must not be called.
The caller may split associated data in segments of any size, and
invoke this method multiple times, each time with the next segment.
:Parameters:
assoc_data : bytes/bytearray/memoryview
A piece of associated data. There are no restrictions on its size.
"""
if self.update not in self._next:
raise TypeError("update() can only be called"
" immediately after initialization")
self._next = [self.update, self.encrypt, self.decrypt,
self.digest, self.verify]
self._signer.update(assoc_data)
return self
def encrypt(self, plaintext, output=None):
"""Encrypt data with the key and the parameters set at initialization.
A cipher object is stateful: once you have encrypted a message
you cannot encrypt (or decrypt) another message using the same
object.
The data to encrypt can be broken up in two or
more pieces and `encrypt` can be called multiple times.
That is, the statement:
>>> c.encrypt(a) + c.encrypt(b)
is equivalent to:
>>> c.encrypt(a+b)
This function does not add any padding to the plaintext.
:Parameters:
plaintext : bytes/bytearray/memoryview
The piece of data to encrypt.
It can be of any length.
:Keywords:
output : bytearray/memoryview
The location where the ciphertext must be written to.
If ``None``, the ciphertext is returned.
:Return:
If ``output`` is ``None``, the ciphertext as ``bytes``.
Otherwise, ``None``.
"""
if self.encrypt not in self._next:
raise TypeError("encrypt() can only be called after"
" initialization or an update()")
self._next = [self.encrypt, self.digest]
ct = self._cipher.encrypt(plaintext, output=output)
if output is None:
self._omac[2].update(ct)
else:
self._omac[2].update(output)
return ct
def decrypt(self, ciphertext, output=None):
"""Decrypt data with the key and the parameters set at initialization.
A cipher object is stateful: once you have decrypted a message
you cannot decrypt (or encrypt) another message with the same
object.
The data to decrypt can be broken up in two or
more pieces and `decrypt` can be called multiple times.
That is, the statement:
>>> c.decrypt(a) + c.decrypt(b)
is equivalent to:
>>> c.decrypt(a+b)
This function does not remove any padding from the plaintext.
:Parameters:
ciphertext : bytes/bytearray/memoryview
The piece of data to decrypt.
It can be of any length.
:Keywords:
output : bytearray/memoryview
The location where the plaintext must be written to.
If ``None``, the plaintext is returned.
:Return:
If ``output`` is ``None``, the plaintext as ``bytes``.
Otherwise, ``None``.
"""
if self.decrypt not in self._next:
raise TypeError("decrypt() can only be called"
" after initialization or an update()")
self._next = [self.decrypt, self.verify]
self._omac[2].update(ciphertext)
return self._cipher.decrypt(ciphertext, output=output)
def digest(self):
"""Compute the *binary* MAC tag.
The caller invokes this function at the very end.
This method returns the MAC that shall be sent to the receiver,
together with the ciphertext.
:Return: the MAC, as a byte string.
"""
if self.digest not in self._next:
raise TypeError("digest() cannot be called when decrypting"
" or validating a message")
self._next = [self.digest]
if not self._mac_tag:
tag = b'\x00' * self.block_size
for i in range(3):
tag = strxor(tag, self._omac[i].digest())
self._mac_tag = tag[:self._mac_len]
return self._mac_tag
def hexdigest(self):
"""Compute the *printable* MAC tag.
This method is like `digest`.
:Return: the MAC, as a hexadecimal string.
"""
return "".join(["%02x" % bord(x) for x in self.digest()])
def verify(self, received_mac_tag):
"""Validate the *binary* MAC tag.
The caller invokes this function at the very end.
This method checks if the decrypted message is indeed valid
(that is, if the key is correct) and it has not been
tampered with while in transit.
:Parameters:
received_mac_tag : bytes/bytearray/memoryview
This is the *binary* MAC, as received from the sender.
:Raises MacMismatchError:
if the MAC does not match. The message has been tampered with
or the key is incorrect.
"""
if self.verify not in self._next:
raise TypeError("verify() cannot be called"
" when encrypting a message")
self._next = [self.verify]
if not self._mac_tag:
tag = b'\x00' * self.block_size
for i in range(3):
tag = strxor(tag, self._omac[i].digest())
self._mac_tag = tag[:self._mac_len]
secret = get_random_bytes(16)
mac1 = BLAKE2s.new(digest_bits=160, key=secret, data=self._mac_tag)
mac2 = BLAKE2s.new(digest_bits=160, key=secret, data=received_mac_tag)
if mac1.digest() != mac2.digest():
raise ValueError("MAC check failed")
def hexverify(self, hex_mac_tag):
"""Validate the *printable* MAC tag.
This method is like `verify`.
:Parameters:
hex_mac_tag : string
This is the *printable* MAC, as received from the sender.
:Raises MacMismatchError:
if the MAC does not match. The message has been tampered with
or the key is incorrect.
"""
self.verify(unhexlify(hex_mac_tag))
def encrypt_and_digest(self, plaintext, output=None):
"""Perform encrypt() and digest() in one step.
:Parameters:
plaintext : bytes/bytearray/memoryview
The piece of data to encrypt.
:Keywords:
output : bytearray/memoryview
The location where the ciphertext must be written to.
If ``None``, the ciphertext is returned.
:Return:
a tuple with two items:
- the ciphertext, as ``bytes``
- the MAC tag, as ``bytes``
The first item becomes ``None`` when the ``output`` parameter
specified a location for the result.
"""
return self.encrypt(plaintext, output=output), self.digest()
def decrypt_and_verify(self, ciphertext, received_mac_tag, output=None):
"""Perform decrypt() and verify() in one step.
:Parameters:
ciphertext : bytes/bytearray/memoryview
The piece of data to decrypt.
received_mac_tag : bytes/bytearray/memoryview
This is the *binary* MAC, as received from the sender.
:Keywords:
output : bytearray/memoryview
The location where the plaintext must be written to.
If ``None``, the plaintext is returned.
:Return: the plaintext as ``bytes`` or ``None`` when the ``output``
parameter specified a location for the result.
:Raises MacMismatchError:
if the MAC does not match. The message has been tampered with
or the key is incorrect.
"""
pt = self.decrypt(ciphertext, output=output)
self.verify(received_mac_tag)
return pt
def _create_eax_cipher(factory, **kwargs):
"""Create a new block cipher, configured in EAX mode.
:Parameters:
factory : module
A symmetric cipher module from `Crypto.Cipher` (like
`Crypto.Cipher.AES`).
:Keywords:
key : bytes/bytearray/memoryview
The secret key to use in the symmetric cipher.
nonce : bytes/bytearray/memoryview
A 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.
The nonce shall never repeat for two different messages encrypted with
the same key, but it does not need to be random.
If not specified, a 16 byte long random string is used.
mac_len : integer
Length of the MAC, in bytes. It must be no larger than the cipher
block bytes (which is the default).
"""
try:
key = kwargs.pop("key")
nonce = kwargs.pop("nonce", None)
if nonce is None:
nonce = get_random_bytes(16)
mac_len = kwargs.pop("mac_len", factory.block_size)
except KeyError as e:
raise TypeError("Missing parameter: " + str(e))
return EaxMode(factory, key, nonce, mac_len, kwargs)

View File

@ -0,0 +1,220 @@
# -*- coding: utf-8 -*-
#
# Cipher/mode_ecb.py : ECB mode
#
# ===================================================================
# 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.
# ===================================================================
"""
Electronic Code Book (ECB) mode.
"""
__all__ = [ 'EcbMode' ]
from Crypto.Util._raw_api import (load_pycryptodome_raw_lib,
VoidPointer, create_string_buffer,
get_raw_buffer, SmartPointer,
c_size_t, c_uint8_ptr,
is_writeable_buffer)
raw_ecb_lib = load_pycryptodome_raw_lib("Crypto.Cipher._raw_ecb", """
int ECB_start_operation(void *cipher,
void **pResult);
int ECB_encrypt(void *ecbState,
const uint8_t *in,
uint8_t *out,
size_t data_len);
int ECB_decrypt(void *ecbState,
const uint8_t *in,
uint8_t *out,
size_t data_len);
int ECB_stop_operation(void *state);
"""
)
class EcbMode(object):
"""*Electronic Code Book (ECB)*.
This is the simplest encryption mode. Each of the plaintext blocks
is directly encrypted into a ciphertext block, independently of
any other block.
This mode is dangerous because it exposes frequency of symbols
in your plaintext. Other modes (e.g. *CBC*) should be used instead.
See `NIST SP800-38A`_ , Section 6.1.
.. _`NIST SP800-38A` : http://csrc.nist.gov/publications/nistpubs/800-38a/sp800-38a.pdf
:undocumented: __init__
"""
def __init__(self, block_cipher):
"""Create a new block cipher, configured in ECB mode.
:Parameters:
block_cipher : C pointer
A smart pointer to the low-level block cipher instance.
"""
self.block_size = block_cipher.block_size
self._state = VoidPointer()
result = raw_ecb_lib.ECB_start_operation(block_cipher.get(),
self._state.address_of())
if result:
raise ValueError("Error %d while instantiating the ECB mode"
% result)
# Ensure that object disposal of this Python object will (eventually)
# free the memory allocated by the raw library for the cipher
# mode
self._state = SmartPointer(self._state.get(),
raw_ecb_lib.ECB_stop_operation)
# Memory allocated for the underlying block cipher is now owned
# by the cipher mode
block_cipher.release()
def encrypt(self, plaintext, output=None):
"""Encrypt data with the key set at initialization.
The data to encrypt can be broken up in two or
more pieces and `encrypt` can be called multiple times.
That is, the statement:
>>> c.encrypt(a) + c.encrypt(b)
is equivalent to:
>>> c.encrypt(a+b)
This function does not add any padding to the plaintext.
:Parameters:
plaintext : bytes/bytearray/memoryview
The piece of data to encrypt.
The length must be multiple of the cipher block length.
:Keywords:
output : bytearray/memoryview
The location where the ciphertext must be written to.
If ``None``, the ciphertext is returned.
:Return:
If ``output`` is ``None``, the ciphertext is returned as ``bytes``.
Otherwise, ``None``.
"""
if output is None:
ciphertext = create_string_buffer(len(plaintext))
else:
ciphertext = output
if not is_writeable_buffer(output):
raise TypeError("output must be a bytearray or a writeable memoryview")
if len(plaintext) != len(output):
raise ValueError("output must have the same length as the input"
" (%d bytes)" % len(plaintext))
result = raw_ecb_lib.ECB_encrypt(self._state.get(),
c_uint8_ptr(plaintext),
c_uint8_ptr(ciphertext),
c_size_t(len(plaintext)))
if result:
if result == 3:
raise ValueError("Data must be aligned to block boundary in ECB mode")
raise ValueError("Error %d while encrypting in ECB mode" % result)
if output is None:
return get_raw_buffer(ciphertext)
else:
return None
def decrypt(self, ciphertext, output=None):
"""Decrypt data with the key set at initialization.
The data to decrypt can be broken up in two or
more pieces and `decrypt` can be called multiple times.
That is, the statement:
>>> c.decrypt(a) + c.decrypt(b)
is equivalent to:
>>> c.decrypt(a+b)
This function does not remove any padding from the plaintext.
:Parameters:
ciphertext : bytes/bytearray/memoryview
The piece of data to decrypt.
The length must be multiple of the cipher block length.
:Keywords:
output : bytearray/memoryview
The location where the plaintext must be written to.
If ``None``, the plaintext is returned.
:Return:
If ``output`` is ``None``, the plaintext is returned as ``bytes``.
Otherwise, ``None``.
"""
if output is None:
plaintext = create_string_buffer(len(ciphertext))
else:
plaintext = output
if not is_writeable_buffer(output):
raise TypeError("output must be a bytearray or a writeable memoryview")
if len(ciphertext) != len(output):
raise ValueError("output must have the same length as the input"
" (%d bytes)" % len(plaintext))
result = raw_ecb_lib.ECB_decrypt(self._state.get(),
c_uint8_ptr(ciphertext),
c_uint8_ptr(plaintext),
c_size_t(len(ciphertext)))
if result:
if result == 3:
raise ValueError("Data must be aligned to block boundary in ECB mode")
raise ValueError("Error %d while decrypting in ECB mode" % result)
if output is None:
return get_raw_buffer(plaintext)
else:
return None
def _create_ecb_cipher(factory, **kwargs):
"""Instantiate a cipher object that performs ECB encryption/decryption.
:Parameters:
factory : module
The underlying block cipher, a module from ``Crypto.Cipher``.
All keywords are passed to the underlying block cipher.
See the relevant documentation for details (at least ``key`` will need
to be present"""
cipher_state = factory._create_base_cipher(kwargs)
cipher_state.block_size = factory.block_size
if kwargs:
raise TypeError("Unknown parameters for ECB: %s" % str(kwargs))
return EcbMode(cipher_state)

View File

@ -0,0 +1,620 @@
# ===================================================================
#
# Copyright (c) 2014, Legrandin <helderijs@gmail.com>
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
#
# 1. Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# 2. Redistributions in binary form must reproduce the above copyright
# notice, this list of conditions and the following disclaimer in
# the documentation and/or other materials provided with the
# distribution.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
# COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.
# ===================================================================
"""
Galois/Counter Mode (GCM).
"""
__all__ = ['GcmMode']
from binascii import unhexlify
from Crypto.Util.py3compat import bord, _copy_bytes
from Crypto.Util._raw_api import is_buffer
from Crypto.Util.number import long_to_bytes, bytes_to_long
from Crypto.Hash import BLAKE2s
from Crypto.Random import get_random_bytes
from Crypto.Util._raw_api import (load_pycryptodome_raw_lib, VoidPointer,
create_string_buffer, get_raw_buffer,
SmartPointer, c_size_t, c_uint8_ptr)
from Crypto.Util import _cpu_features
# C API by module implementing GHASH
_ghash_api_template = """
int ghash_%imp%(uint8_t y_out[16],
const uint8_t block_data[],
size_t len,
const uint8_t y_in[16],
const void *exp_key);
int ghash_expand_%imp%(const uint8_t h[16],
void **ghash_tables);
int ghash_destroy_%imp%(void *ghash_tables);
"""
def _build_impl(lib, postfix):
from collections import namedtuple
funcs = ( "ghash", "ghash_expand", "ghash_destroy" )
GHASH_Imp = namedtuple('_GHash_Imp', funcs)
try:
imp_funcs = [ getattr(lib, x + "_" + postfix) for x in funcs ]
except AttributeError: # Make sphinx stop complaining with its mocklib
imp_funcs = [ None ] * 3
params = dict(zip(funcs, imp_funcs))
return GHASH_Imp(**params)
def _get_ghash_portable():
api = _ghash_api_template.replace("%imp%", "portable")
lib = load_pycryptodome_raw_lib("Crypto.Hash._ghash_portable", api)
result = _build_impl(lib, "portable")
return result
_ghash_portable = _get_ghash_portable()
def _get_ghash_clmul():
"""Return None if CLMUL implementation is not available"""
if not _cpu_features.have_clmul():
return None
try:
api = _ghash_api_template.replace("%imp%", "clmul")
lib = load_pycryptodome_raw_lib("Crypto.Hash._ghash_clmul", api)
result = _build_impl(lib, "clmul")
except OSError:
result = None
return result
_ghash_clmul = _get_ghash_clmul()
class _GHASH(object):
"""GHASH function defined in NIST SP 800-38D, Algorithm 2.
If X_1, X_2, .. X_m are the blocks of input data, the function
computes:
X_1*H^{m} + X_2*H^{m-1} + ... + X_m*H
in the Galois field GF(2^256) using the reducing polynomial
(x^128 + x^7 + x^2 + x + 1).
"""
def __init__(self, subkey, ghash_c):
assert len(subkey) == 16
self.ghash_c = ghash_c
self._exp_key = VoidPointer()
result = ghash_c.ghash_expand(c_uint8_ptr(subkey),
self._exp_key.address_of())
if result:
raise ValueError("Error %d while expanding the GHASH key" % result)
self._exp_key = SmartPointer(self._exp_key.get(),
ghash_c.ghash_destroy)
# create_string_buffer always returns a string of zeroes
self._last_y = create_string_buffer(16)
def update(self, block_data):
assert len(block_data) % 16 == 0
result = self.ghash_c.ghash(self._last_y,
c_uint8_ptr(block_data),
c_size_t(len(block_data)),
self._last_y,
self._exp_key.get())
if result:
raise ValueError("Error %d while updating GHASH" % result)
return self
def digest(self):
return get_raw_buffer(self._last_y)
def enum(**enums):
return type('Enum', (), enums)
MacStatus = enum(PROCESSING_AUTH_DATA=1, PROCESSING_CIPHERTEXT=2)
class GcmMode(object):
"""Galois Counter Mode (GCM).
This is an Authenticated Encryption with Associated Data (`AEAD`_) mode.
It provides both confidentiality and authenticity.
The header of the message may be left in the clear, if needed, and it will
still be subject to authentication. The decryption step tells the receiver
if the message comes from a source that really knowns the secret key.
Additionally, decryption detects if any part of the message - including the
header - has been modified or corrupted.
This mode requires a *nonce*.
This mode is only available for ciphers that operate on 128 bits blocks
(e.g. AES but not TDES).
See `NIST SP800-38D`_.
.. _`NIST SP800-38D`: http://csrc.nist.gov/publications/nistpubs/800-38D/SP-800-38D.pdf
.. _AEAD: http://blog.cryptographyengineering.com/2012/05/how-to-choose-authenticated-encryption.html
:undocumented: __init__
"""
def __init__(self, factory, key, nonce, mac_len, cipher_params, ghash_c):
self.block_size = factory.block_size
if self.block_size != 16:
raise ValueError("GCM mode is only available for ciphers"
" that operate on 128 bits blocks")
if len(nonce) == 0:
raise ValueError("Nonce cannot be empty")
if not is_buffer(nonce):
raise TypeError("Nonce must be bytes, bytearray or memoryview")
# See NIST SP 800 38D, 5.2.1.1
if len(nonce) > 2**64 - 1:
raise ValueError("Nonce exceeds maximum length")
self.nonce = _copy_bytes(None, None, nonce)
"""Nonce"""
self._factory = factory
self._key = _copy_bytes(None, None, key)
self._tag = None # Cache for MAC tag
self._mac_len = mac_len
if not (4 <= mac_len <= 16):
raise ValueError("Parameter 'mac_len' must be in the range 4..16")
# Allowed transitions after initialization
self._next = [self.update, self.encrypt, self.decrypt,
self.digest, self.verify]
self._no_more_assoc_data = False
# Length of associated data
self._auth_len = 0
# Length of the ciphertext or plaintext
self._msg_len = 0
# Step 1 in SP800-38D, Algorithm 4 (encryption) - Compute H
# See also Algorithm 5 (decryption)
hash_subkey = factory.new(key,
self._factory.MODE_ECB,
**cipher_params
).encrypt(b'\x00' * 16)
# Step 2 - Compute J0
if len(self.nonce) == 12:
j0 = self.nonce + b"\x00\x00\x00\x01"
else:
fill = (16 - (len(nonce) % 16)) % 16 + 8
ghash_in = (self.nonce +
b'\x00' * fill +
long_to_bytes(8 * len(nonce), 8))
j0 = _GHASH(hash_subkey, ghash_c).update(ghash_in).digest()
# Step 3 - Prepare GCTR cipher for encryption/decryption
nonce_ctr = j0[:12]
iv_ctr = (bytes_to_long(j0) + 1) & 0xFFFFFFFF
self._cipher = factory.new(key,
self._factory.MODE_CTR,
initial_value=iv_ctr,
nonce=nonce_ctr,
**cipher_params)
# Step 5 - Bootstrat GHASH
self._signer = _GHASH(hash_subkey, ghash_c)
# Step 6 - Prepare GCTR cipher for GMAC
self._tag_cipher = factory.new(key,
self._factory.MODE_CTR,
initial_value=j0,
nonce=b"",
**cipher_params)
# Cache for data to authenticate
self._cache = b""
self._status = MacStatus.PROCESSING_AUTH_DATA
def update(self, assoc_data):
"""Protect associated data
If there is any associated data, the caller has to invoke
this function one or more times, before using
``decrypt`` or ``encrypt``.
By *associated data* it is meant any data (e.g. packet headers) that
will not be encrypted and will be transmitted in the clear.
However, the receiver is still able to detect any modification to it.
In GCM, the *associated data* is also called
*additional authenticated data* (AAD).
If there is no associated data, this method must not be called.
The caller may split associated data in segments of any size, and
invoke this method multiple times, each time with the next segment.
:Parameters:
assoc_data : bytes/bytearray/memoryview
A piece of associated data. There are no restrictions on its size.
"""
if self.update not in self._next:
raise TypeError("update() can only be called"
" immediately after initialization")
self._next = [self.update, self.encrypt, self.decrypt,
self.digest, self.verify]
self._update(assoc_data)
self._auth_len += len(assoc_data)
# See NIST SP 800 38D, 5.2.1.1
if self._auth_len > 2**64 - 1:
raise ValueError("Additional Authenticated Data exceeds maximum length")
return self
def _update(self, data):
assert(len(self._cache) < 16)
if len(self._cache) > 0:
filler = min(16 - len(self._cache), len(data))
self._cache += _copy_bytes(None, filler, data)
data = data[filler:]
if len(self._cache) < 16:
return
# The cache is exactly one block
self._signer.update(self._cache)
self._cache = b""
update_len = len(data) // 16 * 16
self._cache = _copy_bytes(update_len, None, data)
if update_len > 0:
self._signer.update(data[:update_len])
def _pad_cache_and_update(self):
assert(len(self._cache) < 16)
# The authenticated data A is concatenated to the minimum
# number of zero bytes (possibly none) such that the
# - ciphertext C is aligned to the 16 byte boundary.
# See step 5 in section 7.1
# - ciphertext C is aligned to the 16 byte boundary.
# See step 6 in section 7.2
len_cache = len(self._cache)
if len_cache > 0:
self._update(b'\x00' * (16 - len_cache))
def encrypt(self, plaintext, output=None):
"""Encrypt data with the key and the parameters set at initialization.
A cipher object is stateful: once you have encrypted a message
you cannot encrypt (or decrypt) another message using the same
object.
The data to encrypt can be broken up in two or
more pieces and `encrypt` can be called multiple times.
That is, the statement:
>>> c.encrypt(a) + c.encrypt(b)
is equivalent to:
>>> c.encrypt(a+b)
This function does not add any padding to the plaintext.
:Parameters:
plaintext : bytes/bytearray/memoryview
The piece of data to encrypt.
It can be of any length.
:Keywords:
output : bytearray/memoryview
The location where the ciphertext must be written to.
If ``None``, the ciphertext is returned.
:Return:
If ``output`` is ``None``, the ciphertext as ``bytes``.
Otherwise, ``None``.
"""
if self.encrypt not in self._next:
raise TypeError("encrypt() can only be called after"
" initialization or an update()")
self._next = [self.encrypt, self.digest]
ciphertext = self._cipher.encrypt(plaintext, output=output)
if self._status == MacStatus.PROCESSING_AUTH_DATA:
self._pad_cache_and_update()
self._status = MacStatus.PROCESSING_CIPHERTEXT
self._update(ciphertext if output is None else output)
self._msg_len += len(plaintext)
# See NIST SP 800 38D, 5.2.1.1
if self._msg_len > 2**39 - 256:
raise ValueError("Plaintext exceeds maximum length")
return ciphertext
def decrypt(self, ciphertext, output=None):
"""Decrypt data with the key and the parameters set at initialization.
A cipher object is stateful: once you have decrypted a message
you cannot decrypt (or encrypt) another message with the same
object.
The data to decrypt can be broken up in two or
more pieces and `decrypt` can be called multiple times.
That is, the statement:
>>> c.decrypt(a) + c.decrypt(b)
is equivalent to:
>>> c.decrypt(a+b)
This function does not remove any padding from the plaintext.
:Parameters:
ciphertext : bytes/bytearray/memoryview
The piece of data to decrypt.
It can be of any length.
:Keywords:
output : bytearray/memoryview
The location where the plaintext must be written to.
If ``None``, the plaintext is returned.
:Return:
If ``output`` is ``None``, the plaintext as ``bytes``.
Otherwise, ``None``.
"""
if self.decrypt not in self._next:
raise TypeError("decrypt() can only be called"
" after initialization or an update()")
self._next = [self.decrypt, self.verify]
if self._status == MacStatus.PROCESSING_AUTH_DATA:
self._pad_cache_and_update()
self._status = MacStatus.PROCESSING_CIPHERTEXT
self._update(ciphertext)
self._msg_len += len(ciphertext)
return self._cipher.decrypt(ciphertext, output=output)
def digest(self):
"""Compute the *binary* MAC tag in an AEAD mode.
The caller invokes this function at the very end.
This method returns the MAC that shall be sent to the receiver,
together with the ciphertext.
:Return: the MAC, as a byte string.
"""
if self.digest not in self._next:
raise TypeError("digest() cannot be called when decrypting"
" or validating a message")
self._next = [self.digest]
return self._compute_mac()
def _compute_mac(self):
"""Compute MAC without any FSM checks."""
if self._tag:
return self._tag
# Step 5 in NIST SP 800-38D, Algorithm 4 - Compute S
self._pad_cache_and_update()
self._update(long_to_bytes(8 * self._auth_len, 8))
self._update(long_to_bytes(8 * self._msg_len, 8))
s_tag = self._signer.digest()
# Step 6 - Compute T
self._tag = self._tag_cipher.encrypt(s_tag)[:self._mac_len]
return self._tag
def hexdigest(self):
"""Compute the *printable* MAC tag.
This method is like `digest`.
:Return: the MAC, as a hexadecimal string.
"""
return "".join(["%02x" % bord(x) for x in self.digest()])
def verify(self, received_mac_tag):
"""Validate the *binary* MAC tag.
The caller invokes this function at the very end.
This method checks if the decrypted message is indeed valid
(that is, if the key is correct) and it has not been
tampered with while in transit.
:Parameters:
received_mac_tag : bytes/bytearray/memoryview
This is the *binary* MAC, as received from the sender.
:Raises ValueError:
if the MAC does not match. The message has been tampered with
or the key is incorrect.
"""
if self.verify not in self._next:
raise TypeError("verify() cannot be called"
" when encrypting a message")
self._next = [self.verify]
secret = get_random_bytes(16)
mac1 = BLAKE2s.new(digest_bits=160, key=secret,
data=self._compute_mac())
mac2 = BLAKE2s.new(digest_bits=160, key=secret,
data=received_mac_tag)
if mac1.digest() != mac2.digest():
raise ValueError("MAC check failed")
def hexverify(self, hex_mac_tag):
"""Validate the *printable* MAC tag.
This method is like `verify`.
:Parameters:
hex_mac_tag : string
This is the *printable* MAC, as received from the sender.
:Raises ValueError:
if the MAC does not match. The message has been tampered with
or the key is incorrect.
"""
self.verify(unhexlify(hex_mac_tag))
def encrypt_and_digest(self, plaintext, output=None):
"""Perform encrypt() and digest() in one step.
:Parameters:
plaintext : bytes/bytearray/memoryview
The piece of data to encrypt.
:Keywords:
output : bytearray/memoryview
The location where the ciphertext must be written to.
If ``None``, the ciphertext is returned.
:Return:
a tuple with two items:
- the ciphertext, as ``bytes``
- the MAC tag, as ``bytes``
The first item becomes ``None`` when the ``output`` parameter
specified a location for the result.
"""
return self.encrypt(plaintext, output=output), self.digest()
def decrypt_and_verify(self, ciphertext, received_mac_tag, output=None):
"""Perform decrypt() and verify() in one step.
:Parameters:
ciphertext : bytes/bytearray/memoryview
The piece of data to decrypt.
received_mac_tag : byte string
This is the *binary* MAC, as received from the sender.
:Keywords:
output : bytearray/memoryview
The location where the plaintext must be written to.
If ``None``, the plaintext is returned.
:Return: the plaintext as ``bytes`` or ``None`` when the ``output``
parameter specified a location for the result.
:Raises ValueError:
if the MAC does not match. The message has been tampered with
or the key is incorrect.
"""
plaintext = self.decrypt(ciphertext, output=output)
self.verify(received_mac_tag)
return plaintext
def _create_gcm_cipher(factory, **kwargs):
"""Create a new block cipher, configured in Galois Counter Mode (GCM).
:Parameters:
factory : module
A block cipher module, taken from `Crypto.Cipher`.
The cipher must have block length of 16 bytes.
GCM has been only defined for `Crypto.Cipher.AES`.
:Keywords:
key : bytes/bytearray/memoryview
The secret key to use in the symmetric cipher.
It must be 16 (e.g. *AES-128*), 24 (e.g. *AES-192*)
or 32 (e.g. *AES-256*) bytes long.
nonce : bytes/bytearray/memoryview
A 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.
The nonce shall never repeat for two
different messages encrypted with the same key,
but it does not need to be random.
If not provided, a 16 byte nonce will be randomly created.
mac_len : integer
Length of the MAC, in bytes.
It must be no larger than 16 bytes (which is the default).
"""
try:
key = kwargs.pop("key")
except KeyError as e:
raise TypeError("Missing parameter:" + str(e))
nonce = kwargs.pop("nonce", None)
if nonce is None:
nonce = get_random_bytes(16)
mac_len = kwargs.pop("mac_len", 16)
# Not documented - only used for testing
use_clmul = kwargs.pop("use_clmul", True)
if use_clmul and _ghash_clmul:
ghash_c = _ghash_clmul
else:
ghash_c = _ghash_portable
return GcmMode(factory, key, nonce, mac_len, kwargs, ghash_c)

View File

@ -0,0 +1,525 @@
# ===================================================================
#
# Copyright (c) 2014, Legrandin <helderijs@gmail.com>
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
#
# 1. Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# 2. Redistributions in binary form must reproduce the above copyright
# notice, this list of conditions and the following disclaimer in
# the documentation and/or other materials provided with the
# distribution.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
# COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.
# ===================================================================
"""
Offset Codebook (OCB) mode.
OCB is Authenticated Encryption with Associated Data (AEAD) cipher mode
designed by Prof. Phillip Rogaway and specified in `RFC7253`_.
The algorithm provides both authenticity and privacy, it is very efficient,
it uses only one key and it can be used in online mode (so that encryption
or decryption can start before the end of the message is available).
This module implements the third and last variant of OCB (OCB3) and it only
works in combination with a 128-bit block symmetric cipher, like AES.
OCB is patented in US but `free licenses`_ exist for software implementations
meant for non-military purposes.
Example:
>>> from Crypto.Cipher import AES
>>> from Crypto.Random import get_random_bytes
>>>
>>> key = get_random_bytes(32)
>>> cipher = AES.new(key, AES.MODE_OCB)
>>> plaintext = b"Attack at dawn"
>>> ciphertext, mac = cipher.encrypt_and_digest(plaintext)
>>> # Deliver cipher.nonce, ciphertext and mac
...
>>> cipher = AES.new(key, AES.MODE_OCB, nonce=nonce)
>>> try:
>>> plaintext = cipher.decrypt_and_verify(ciphertext, mac)
>>> except ValueError:
>>> print "Invalid message"
>>> else:
>>> print plaintext
:undocumented: __package__
.. _RFC7253: http://www.rfc-editor.org/info/rfc7253
.. _free licenses: http://web.cs.ucdavis.edu/~rogaway/ocb/license.htm
"""
import struct
from binascii import unhexlify
from Crypto.Util.py3compat import bord, _copy_bytes
from Crypto.Util.number import long_to_bytes, bytes_to_long
from Crypto.Util.strxor import strxor
from Crypto.Hash import BLAKE2s
from Crypto.Random import get_random_bytes
from Crypto.Util._raw_api import (load_pycryptodome_raw_lib, VoidPointer,
create_string_buffer, get_raw_buffer,
SmartPointer, c_size_t, c_uint8_ptr,
is_buffer)
_raw_ocb_lib = load_pycryptodome_raw_lib("Crypto.Cipher._raw_ocb", """
int OCB_start_operation(void *cipher,
const uint8_t *offset_0,
size_t offset_0_len,
void **pState);
int OCB_encrypt(void *state,
const uint8_t *in,
uint8_t *out,
size_t data_len);
int OCB_decrypt(void *state,
const uint8_t *in,
uint8_t *out,
size_t data_len);
int OCB_update(void *state,
const uint8_t *in,
size_t data_len);
int OCB_digest(void *state,
uint8_t *tag,
size_t tag_len);
int OCB_stop_operation(void *state);
""")
class OcbMode(object):
"""Offset Codebook (OCB) mode.
:undocumented: __init__
"""
def __init__(self, factory, nonce, mac_len, cipher_params):
if factory.block_size != 16:
raise ValueError("OCB mode is only available for ciphers"
" that operate on 128 bits blocks")
self.block_size = 16
"""The block size of the underlying cipher, in bytes."""
self.nonce = _copy_bytes(None, None, nonce)
"""Nonce used for this session."""
if len(nonce) not in range(1, 16):
raise ValueError("Nonce must be at most 15 bytes long")
if not is_buffer(nonce):
raise TypeError("Nonce must be bytes, bytearray or memoryview")
self._mac_len = mac_len
if not 8 <= mac_len <= 16:
raise ValueError("MAC tag must be between 8 and 16 bytes long")
# Cache for MAC tag
self._mac_tag = None
# Cache for unaligned associated data
self._cache_A = b""
# Cache for unaligned ciphertext/plaintext
self._cache_P = b""
# Allowed transitions after initialization
self._next = [self.update, self.encrypt, self.decrypt,
self.digest, self.verify]
# Compute Offset_0
params_without_key = dict(cipher_params)
key = params_without_key.pop("key")
nonce = (struct.pack('B', self._mac_len << 4 & 0xFF) +
b'\x00' * (14 - len(nonce)) +
b'\x01' + self.nonce)
bottom_bits = bord(nonce[15]) & 0x3F # 6 bits, 0..63
top_bits = bord(nonce[15]) & 0xC0 # 2 bits
ktop_cipher = factory.new(key,
factory.MODE_ECB,
**params_without_key)
ktop = ktop_cipher.encrypt(struct.pack('15sB',
nonce[:15],
top_bits))
stretch = ktop + strxor(ktop[:8], ktop[1:9]) # 192 bits
offset_0 = long_to_bytes(bytes_to_long(stretch) >>
(64 - bottom_bits), 24)[8:]
# Create low-level cipher instance
raw_cipher = factory._create_base_cipher(cipher_params)
if cipher_params:
raise TypeError("Unknown keywords: " + str(cipher_params))
self._state = VoidPointer()
result = _raw_ocb_lib.OCB_start_operation(raw_cipher.get(),
offset_0,
c_size_t(len(offset_0)),
self._state.address_of())
if result:
raise ValueError("Error %d while instantiating the OCB mode"
% result)
# Ensure that object disposal of this Python object will (eventually)
# free the memory allocated by the raw library for the cipher mode
self._state = SmartPointer(self._state.get(),
_raw_ocb_lib.OCB_stop_operation)
# Memory allocated for the underlying block cipher is now owed
# by the cipher mode
raw_cipher.release()
def _update(self, assoc_data, assoc_data_len):
result = _raw_ocb_lib.OCB_update(self._state.get(),
c_uint8_ptr(assoc_data),
c_size_t(assoc_data_len))
if result:
raise ValueError("Error %d while computing MAC in OCB mode" % result)
def update(self, assoc_data):
"""Process the associated data.
If there is any associated data, the caller has to invoke
this method one or more times, before using
``decrypt`` or ``encrypt``.
By *associated data* it is meant any data (e.g. packet headers) that
will not be encrypted and will be transmitted in the clear.
However, the receiver shall still able to detect modifications.
If there is no associated data, this method must not be called.
The caller may split associated data in segments of any size, and
invoke this method multiple times, each time with the next segment.
:Parameters:
assoc_data : bytes/bytearray/memoryview
A piece of associated data.
"""
if self.update not in self._next:
raise TypeError("update() can only be called"
" immediately after initialization")
self._next = [self.encrypt, self.decrypt, self.digest,
self.verify, self.update]
if len(self._cache_A) > 0:
filler = min(16 - len(self._cache_A), len(assoc_data))
self._cache_A += _copy_bytes(None, filler, assoc_data)
assoc_data = assoc_data[filler:]
if len(self._cache_A) < 16:
return self
# Clear the cache, and proceeding with any other aligned data
self._cache_A, seg = b"", self._cache_A
self.update(seg)
update_len = len(assoc_data) // 16 * 16
self._cache_A = _copy_bytes(update_len, None, assoc_data)
self._update(assoc_data, update_len)
return self
def _transcrypt_aligned(self, in_data, in_data_len,
trans_func, trans_desc):
out_data = create_string_buffer(in_data_len)
result = trans_func(self._state.get(),
in_data,
out_data,
c_size_t(in_data_len))
if result:
raise ValueError("Error %d while %sing in OCB mode"
% (result, trans_desc))
return get_raw_buffer(out_data)
def _transcrypt(self, in_data, trans_func, trans_desc):
# Last piece to encrypt/decrypt
if in_data is None:
out_data = self._transcrypt_aligned(self._cache_P,
len(self._cache_P),
trans_func,
trans_desc)
self._cache_P = b""
return out_data
# Try to fill up the cache, if it already contains something
prefix = b""
if len(self._cache_P) > 0:
filler = min(16 - len(self._cache_P), len(in_data))
self._cache_P += _copy_bytes(None, filler, in_data)
in_data = in_data[filler:]
if len(self._cache_P) < 16:
# We could not manage to fill the cache, so there is certainly
# no output yet.
return b""
# Clear the cache, and proceeding with any other aligned data
prefix = self._transcrypt_aligned(self._cache_P,
len(self._cache_P),
trans_func,
trans_desc)
self._cache_P = b""
# Process data in multiples of the block size
trans_len = len(in_data) // 16 * 16
result = self._transcrypt_aligned(c_uint8_ptr(in_data),
trans_len,
trans_func,
trans_desc)
if prefix:
result = prefix + result
# Left-over
self._cache_P = _copy_bytes(trans_len, None, in_data)
return result
def encrypt(self, plaintext=None):
"""Encrypt the next piece of plaintext.
After the entire plaintext has been passed (but before `digest`),
you **must** call this method one last time with no arguments to collect
the final piece of ciphertext.
If possible, use the method `encrypt_and_digest` instead.
:Parameters:
plaintext : bytes/bytearray/memoryview
The next piece of data to encrypt or ``None`` to signify
that encryption has finished and that any remaining ciphertext
has to be produced.
:Return:
the ciphertext, as a byte string.
Its length may not match the length of the *plaintext*.
"""
if self.encrypt not in self._next:
raise TypeError("encrypt() can only be called after"
" initialization or an update()")
if plaintext is None:
self._next = [self.digest]
else:
self._next = [self.encrypt]
return self._transcrypt(plaintext, _raw_ocb_lib.OCB_encrypt, "encrypt")
def decrypt(self, ciphertext=None):
"""Decrypt the next piece of ciphertext.
After the entire ciphertext has been passed (but before `verify`),
you **must** call this method one last time with no arguments to collect
the remaining piece of plaintext.
If possible, use the method `decrypt_and_verify` instead.
:Parameters:
ciphertext : bytes/bytearray/memoryview
The next piece of data to decrypt or ``None`` to signify
that decryption has finished and that any remaining plaintext
has to be produced.
:Return:
the plaintext, as a byte string.
Its length may not match the length of the *ciphertext*.
"""
if self.decrypt not in self._next:
raise TypeError("decrypt() can only be called after"
" initialization or an update()")
if ciphertext is None:
self._next = [self.verify]
else:
self._next = [self.decrypt]
return self._transcrypt(ciphertext,
_raw_ocb_lib.OCB_decrypt,
"decrypt")
def _compute_mac_tag(self):
if self._mac_tag is not None:
return
if self._cache_A:
self._update(self._cache_A, len(self._cache_A))
self._cache_A = b""
mac_tag = create_string_buffer(16)
result = _raw_ocb_lib.OCB_digest(self._state.get(),
mac_tag,
c_size_t(len(mac_tag))
)
if result:
raise ValueError("Error %d while computing digest in OCB mode"
% result)
self._mac_tag = get_raw_buffer(mac_tag)[:self._mac_len]
def digest(self):
"""Compute the *binary* MAC tag.
Call this method after the final `encrypt` (the one with no arguments)
to obtain the MAC tag.
The MAC tag is needed by the receiver to determine authenticity
of the message.
:Return: the MAC, as a byte string.
"""
if self.digest not in self._next:
raise TypeError("digest() cannot be called now for this cipher")
assert(len(self._cache_P) == 0)
self._next = [self.digest]
if self._mac_tag is None:
self._compute_mac_tag()
return self._mac_tag
def hexdigest(self):
"""Compute the *printable* MAC tag.
This method is like `digest`.
:Return: the MAC, as a hexadecimal string.
"""
return "".join(["%02x" % bord(x) for x in self.digest()])
def verify(self, received_mac_tag):
"""Validate the *binary* MAC tag.
Call this method after the final `decrypt` (the one with no arguments)
to check if the message is authentic and valid.
:Parameters:
received_mac_tag : bytes/bytearray/memoryview
This is the *binary* MAC, as received from the sender.
:Raises ValueError:
if the MAC does not match. The message has been tampered with
or the key is incorrect.
"""
if self.verify not in self._next:
raise TypeError("verify() cannot be called now for this cipher")
assert(len(self._cache_P) == 0)
self._next = [self.verify]
if self._mac_tag is None:
self._compute_mac_tag()
secret = get_random_bytes(16)
mac1 = BLAKE2s.new(digest_bits=160, key=secret, data=self._mac_tag)
mac2 = BLAKE2s.new(digest_bits=160, key=secret, data=received_mac_tag)
if mac1.digest() != mac2.digest():
raise ValueError("MAC check failed")
def hexverify(self, hex_mac_tag):
"""Validate the *printable* MAC tag.
This method is like `verify`.
:Parameters:
hex_mac_tag : string
This is the *printable* MAC, as received from the sender.
:Raises ValueError:
if the MAC does not match. The message has been tampered with
or the key is incorrect.
"""
self.verify(unhexlify(hex_mac_tag))
def encrypt_and_digest(self, plaintext):
"""Encrypt the message and create the MAC tag in one step.
:Parameters:
plaintext : bytes/bytearray/memoryview
The entire message to encrypt.
:Return:
a tuple with two byte strings:
- the encrypted data
- the MAC
"""
return self.encrypt(plaintext) + self.encrypt(), self.digest()
def decrypt_and_verify(self, ciphertext, received_mac_tag):
"""Decrypted the message and verify its authenticity in one step.
:Parameters:
ciphertext : bytes/bytearray/memoryview
The entire message to decrypt.
received_mac_tag : byte string
This is the *binary* MAC, as received from the sender.
:Return: the decrypted data (byte string).
:Raises ValueError:
if the MAC does not match. The message has been tampered with
or the key is incorrect.
"""
plaintext = self.decrypt(ciphertext) + self.decrypt()
self.verify(received_mac_tag)
return plaintext
def _create_ocb_cipher(factory, **kwargs):
"""Create a new block cipher, configured in OCB mode.
:Parameters:
factory : module
A symmetric cipher module from `Crypto.Cipher`
(like `Crypto.Cipher.AES`).
:Keywords:
nonce : bytes/bytearray/memoryview
A value that must never be reused for any other encryption.
Its length can vary from 1 to 15 bytes.
If not specified, a random 15 bytes long nonce is generated.
mac_len : integer
Length of the MAC, in bytes.
It must be in the range ``[8..16]``.
The default is 16 (128 bits).
Any other keyword will be passed to the underlying block cipher.
See the relevant documentation for details (at least ``key`` will need
to be present).
"""
try:
nonce = kwargs.pop("nonce", None)
if nonce is None:
nonce = get_random_bytes(15)
mac_len = kwargs.pop("mac_len", 16)
except KeyError as e:
raise TypeError("Keyword missing: " + str(e))
return OcbMode(factory, nonce, mac_len, kwargs)

View File

@ -0,0 +1,282 @@
# -*- coding: utf-8 -*-
#
# Cipher/mode_ofb.py : OFB mode
#
# ===================================================================
# 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.
# ===================================================================
"""
Output Feedback (CFB) mode.
"""
__all__ = ['OfbMode']
from Crypto.Util.py3compat import _copy_bytes
from Crypto.Util._raw_api import (load_pycryptodome_raw_lib, VoidPointer,
create_string_buffer, get_raw_buffer,
SmartPointer, c_size_t, c_uint8_ptr,
is_writeable_buffer)
from Crypto.Random import get_random_bytes
raw_ofb_lib = load_pycryptodome_raw_lib("Crypto.Cipher._raw_ofb", """
int OFB_start_operation(void *cipher,
const uint8_t iv[],
size_t iv_len,
void **pResult);
int OFB_encrypt(void *ofbState,
const uint8_t *in,
uint8_t *out,
size_t data_len);
int OFB_decrypt(void *ofbState,
const uint8_t *in,
uint8_t *out,
size_t data_len);
int OFB_stop_operation(void *state);
"""
)
class OfbMode(object):
"""*Output FeedBack (OFB)*.
This mode is very similar to CBC, but it
transforms the underlying block cipher into a stream cipher.
The keystream is the iterated block encryption of the
previous ciphertext block.
An Initialization Vector (*IV*) is required.
See `NIST SP800-38A`_ , Section 6.4.
.. _`NIST SP800-38A` : http://csrc.nist.gov/publications/nistpubs/800-38a/sp800-38a.pdf
:undocumented: __init__
"""
def __init__(self, block_cipher, iv):
"""Create a new block cipher, configured in OFB mode.
:Parameters:
block_cipher : C pointer
A smart pointer to the low-level block cipher instance.
iv : bytes/bytearray/memoryview
The initialization vector to use for encryption or decryption.
It is as long as the cipher block.
**The IV must be a nonce, to to be reused for any other
message**. It shall be a nonce or a random value.
Reusing the *IV* for encryptions performed with the same key
compromises confidentiality.
"""
self._state = VoidPointer()
result = raw_ofb_lib.OFB_start_operation(block_cipher.get(),
c_uint8_ptr(iv),
c_size_t(len(iv)),
self._state.address_of())
if result:
raise ValueError("Error %d while instantiating the OFB mode"
% result)
# Ensure that object disposal of this Python object will (eventually)
# free the memory allocated by the raw library for the cipher mode
self._state = SmartPointer(self._state.get(),
raw_ofb_lib.OFB_stop_operation)
# Memory allocated for the underlying block cipher is now owed
# by the cipher mode
block_cipher.release()
self.block_size = len(iv)
"""The block size of the underlying cipher, in bytes."""
self.iv = _copy_bytes(None, None, iv)
"""The Initialization Vector originally used to create the object.
The value does not change."""
self.IV = self.iv
"""Alias for `iv`"""
self._next = [ self.encrypt, self.decrypt ]
def encrypt(self, plaintext, output=None):
"""Encrypt data with the key and the parameters set at initialization.
A cipher object is stateful: once you have encrypted a message
you cannot encrypt (or decrypt) another message using the same
object.
The data to encrypt can be broken up in two or
more pieces and `encrypt` can be called multiple times.
That is, the statement:
>>> c.encrypt(a) + c.encrypt(b)
is equivalent to:
>>> c.encrypt(a+b)
This function does not add any padding to the plaintext.
:Parameters:
plaintext : bytes/bytearray/memoryview
The piece of data to encrypt.
It can be of any length.
:Keywords:
output : bytearray/memoryview
The location where the ciphertext must be written to.
If ``None``, the ciphertext is returned.
:Return:
If ``output`` is ``None``, the ciphertext is returned as ``bytes``.
Otherwise, ``None``.
"""
if self.encrypt not in self._next:
raise TypeError("encrypt() cannot be called after decrypt()")
self._next = [ self.encrypt ]
if output is None:
ciphertext = create_string_buffer(len(plaintext))
else:
ciphertext = output
if not is_writeable_buffer(output):
raise TypeError("output must be a bytearray or a writeable memoryview")
if len(plaintext) != len(output):
raise ValueError("output must have the same length as the input"
" (%d bytes)" % len(plaintext))
result = raw_ofb_lib.OFB_encrypt(self._state.get(),
c_uint8_ptr(plaintext),
c_uint8_ptr(ciphertext),
c_size_t(len(plaintext)))
if result:
raise ValueError("Error %d while encrypting in OFB mode" % result)
if output is None:
return get_raw_buffer(ciphertext)
else:
return None
def decrypt(self, ciphertext, output=None):
"""Decrypt data with the key and the parameters set at initialization.
A cipher object is stateful: once you have decrypted a message
you cannot decrypt (or encrypt) another message with the same
object.
The data to decrypt can be broken up in two or
more pieces and `decrypt` can be called multiple times.
That is, the statement:
>>> c.decrypt(a) + c.decrypt(b)
is equivalent to:
>>> c.decrypt(a+b)
This function does not remove any padding from the plaintext.
:Parameters:
ciphertext : bytes/bytearray/memoryview
The piece of data to decrypt.
It can be of any length.
:Keywords:
output : bytearray/memoryview
The location where the plaintext is written to.
If ``None``, the plaintext is returned.
:Return:
If ``output`` is ``None``, the plaintext is returned as ``bytes``.
Otherwise, ``None``.
"""
if self.decrypt not in self._next:
raise TypeError("decrypt() cannot be called after encrypt()")
self._next = [ self.decrypt ]
if output is None:
plaintext = create_string_buffer(len(ciphertext))
else:
plaintext = output
if not is_writeable_buffer(output):
raise TypeError("output must be a bytearray or a writeable memoryview")
if len(ciphertext) != len(output):
raise ValueError("output must have the same length as the input"
" (%d bytes)" % len(plaintext))
result = raw_ofb_lib.OFB_decrypt(self._state.get(),
c_uint8_ptr(ciphertext),
c_uint8_ptr(plaintext),
c_size_t(len(ciphertext)))
if result:
raise ValueError("Error %d while decrypting in OFB mode" % result)
if output is None:
return get_raw_buffer(plaintext)
else:
return None
def _create_ofb_cipher(factory, **kwargs):
"""Instantiate a cipher object that performs OFB encryption/decryption.
:Parameters:
factory : module
The underlying block cipher, a module from ``Crypto.Cipher``.
:Keywords:
iv : bytes/bytearray/memoryview
The IV to use for OFB.
IV : bytes/bytearray/memoryview
Alias for ``iv``.
Any other keyword will be passed to the underlying block cipher.
See the relevant documentation for details (at least ``key`` will need
to be present).
"""
cipher_state = factory._create_base_cipher(kwargs)
iv = kwargs.pop("IV", None)
IV = kwargs.pop("iv", None)
if (None, None) == (iv, IV):
iv = get_random_bytes(factory.block_size)
if iv is not None:
if IV is not None:
raise TypeError("You must either use 'iv' or 'IV', not both")
else:
iv = IV
if len(iv) != factory.block_size:
raise ValueError("Incorrect IV length (it must be %d bytes long)" %
factory.block_size)
if kwargs:
raise TypeError("Unknown parameters for OFB: %s" % str(kwargs))
return OfbMode(cipher_state, iv)

View File

@ -0,0 +1,206 @@
# ===================================================================
#
# Copyright (c) 2014, Legrandin <helderijs@gmail.com>
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
#
# 1. Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# 2. Redistributions in binary form must reproduce the above copyright
# notice, this list of conditions and the following disclaimer in
# the documentation and/or other materials provided with the
# distribution.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
# COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.
# ===================================================================
"""
OpenPGP mode.
"""
__all__ = ['OpenPgpMode']
from Crypto.Util.py3compat import _copy_bytes
from Crypto.Random import get_random_bytes
class OpenPgpMode(object):
"""OpenPGP mode.
This mode is a variant of CFB, and it is only used in PGP and
OpenPGP_ applications. If in doubt, use another mode.
An Initialization Vector (*IV*) is required.
Unlike CFB, the *encrypted* IV (not the IV itself) is
transmitted to the receiver.
The IV is a random data block. For legacy reasons, two of its bytes are
duplicated to act as a checksum for the correctness of the key, which is now
known to be insecure and is ignored. The encrypted IV is therefore 2 bytes
longer than the clean IV.
.. _OpenPGP: http://tools.ietf.org/html/rfc4880
:undocumented: __init__
"""
def __init__(self, factory, key, iv, cipher_params):
#: The block size of the underlying cipher, in bytes.
self.block_size = factory.block_size
self._done_first_block = False # True after the first encryption
# Instantiate a temporary cipher to process the IV
IV_cipher = factory.new(
key,
factory.MODE_CFB,
IV=b'\x00' * self.block_size,
segment_size=self.block_size * 8,
**cipher_params)
iv = _copy_bytes(None, None, iv)
# The cipher will be used for...
if len(iv) == self.block_size:
# ... encryption
self._encrypted_IV = IV_cipher.encrypt(iv + iv[-2:])
elif len(iv) == self.block_size + 2:
# ... decryption
self._encrypted_IV = iv
# Last two bytes are for a deprecated "quick check" feature that
# should not be used. (https://eprint.iacr.org/2005/033)
iv = IV_cipher.decrypt(iv)[:-2]
else:
raise ValueError("Length of IV must be %d or %d bytes"
" for MODE_OPENPGP"
% (self.block_size, self.block_size + 2))
self.iv = self.IV = iv
# Instantiate the cipher for the real PGP data
self._cipher = factory.new(
key,
factory.MODE_CFB,
IV=self._encrypted_IV[-self.block_size:],
segment_size=self.block_size * 8,
**cipher_params)
def encrypt(self, plaintext):
"""Encrypt data with the key and the parameters set at initialization.
A cipher object is stateful: once you have encrypted a message
you cannot encrypt (or decrypt) another message using the same
object.
The data to encrypt can be broken up in two or
more pieces and `encrypt` can be called multiple times.
That is, the statement:
>>> c.encrypt(a) + c.encrypt(b)
is equivalent to:
>>> c.encrypt(a+b)
This function does not add any padding to the plaintext.
:Parameters:
plaintext : bytes/bytearray/memoryview
The piece of data to encrypt.
:Return:
the encrypted data, as a byte string.
It is as long as *plaintext* with one exception:
when encrypting the first message chunk,
the encypted IV is prepended to the returned ciphertext.
"""
res = self._cipher.encrypt(plaintext)
if not self._done_first_block:
res = self._encrypted_IV + res
self._done_first_block = True
return res
def decrypt(self, ciphertext):
"""Decrypt data with the key and the parameters set at initialization.
A cipher object is stateful: once you have decrypted a message
you cannot decrypt (or encrypt) another message with the same
object.
The data to decrypt can be broken up in two or
more pieces and `decrypt` can be called multiple times.
That is, the statement:
>>> c.decrypt(a) + c.decrypt(b)
is equivalent to:
>>> c.decrypt(a+b)
This function does not remove any padding from the plaintext.
:Parameters:
ciphertext : bytes/bytearray/memoryview
The piece of data to decrypt.
:Return: the decrypted data (byte string).
"""
return self._cipher.decrypt(ciphertext)
def _create_openpgp_cipher(factory, **kwargs):
"""Create a new block cipher, configured in OpenPGP mode.
:Parameters:
factory : module
The module.
:Keywords:
key : bytes/bytearray/memoryview
The secret key to use in the symmetric cipher.
IV : bytes/bytearray/memoryview
The initialization vector to use for encryption or decryption.
For encryption, the IV must be as long as the cipher block size.
For decryption, it must be 2 bytes longer (it is actually the
*encrypted* IV which was prefixed to the ciphertext).
"""
iv = kwargs.pop("IV", None)
IV = kwargs.pop("iv", None)
if (None, None) == (iv, IV):
iv = get_random_bytes(factory.block_size)
if iv is not None:
if IV is not None:
raise TypeError("You must either use 'iv' or 'IV', not both")
else:
iv = IV
try:
key = kwargs.pop("key")
except KeyError as e:
raise TypeError("Missing component: " + str(e))
return OpenPgpMode(factory, key, iv, kwargs)

View File

@ -0,0 +1,392 @@
# ===================================================================
#
# Copyright (c) 2014, Legrandin <helderijs@gmail.com>
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
#
# 1. Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# 2. Redistributions in binary form must reproduce the above copyright
# notice, this list of conditions and the following disclaimer in
# the documentation and/or other materials provided with the
# distribution.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
# COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.
# ===================================================================
"""
Synthetic Initialization Vector (SIV) mode.
"""
__all__ = ['SivMode']
from binascii import hexlify, unhexlify
from Crypto.Util.py3compat import bord, _copy_bytes
from Crypto.Util._raw_api import is_buffer
from Crypto.Util.number import long_to_bytes, bytes_to_long
from Crypto.Protocol.KDF import _S2V
from Crypto.Hash import BLAKE2s
from Crypto.Random import get_random_bytes
class SivMode(object):
"""Synthetic Initialization Vector (SIV).
This is an Authenticated Encryption with Associated Data (`AEAD`_) mode.
It provides both confidentiality and authenticity.
The header of the message may be left in the clear, if needed, and it will
still be subject to authentication. The decryption step tells the receiver
if the message comes from a source that really knowns the secret key.
Additionally, decryption detects if any part of the message - including the
header - has been modified or corrupted.
Unlike other AEAD modes such as CCM, EAX or GCM, accidental reuse of a
nonce is not catastrophic for the confidentiality of the message. The only
effect is that an attacker can tell when the same plaintext (and same
associated data) is protected with the same key.
The length of the MAC is fixed to the block size of the underlying cipher.
The key size is twice the length of the key of the underlying cipher.
This mode is only available for AES ciphers.
+--------------------+---------------+-------------------+
| Cipher | SIV MAC size | SIV key length |
| | (bytes) | (bytes) |
+====================+===============+===================+
| AES-128 | 16 | 32 |
+--------------------+---------------+-------------------+
| AES-192 | 16 | 48 |
+--------------------+---------------+-------------------+
| AES-256 | 16 | 64 |
+--------------------+---------------+-------------------+
See `RFC5297`_ and the `original paper`__.
.. _RFC5297: https://tools.ietf.org/html/rfc5297
.. _AEAD: http://blog.cryptographyengineering.com/2012/05/how-to-choose-authenticated-encryption.html
.. __: http://www.cs.ucdavis.edu/~rogaway/papers/keywrap.pdf
:undocumented: __init__
"""
def __init__(self, factory, key, nonce, kwargs):
self.block_size = factory.block_size
"""The block size of the underlying cipher, in bytes."""
self._factory = factory
self._cipher_params = kwargs
if len(key) not in (32, 48, 64):
raise ValueError("Incorrect key length (%d bytes)" % len(key))
if nonce is not None:
if not is_buffer(nonce):
raise TypeError("When provided, the nonce must be bytes, bytearray or memoryview")
if len(nonce) == 0:
raise ValueError("When provided, the nonce must be non-empty")
self.nonce = _copy_bytes(None, None, nonce)
"""Public attribute is only available in case of non-deterministic
encryption."""
subkey_size = len(key) // 2
self._mac_tag = None # Cache for MAC tag
self._kdf = _S2V(key[:subkey_size],
ciphermod=factory,
cipher_params=self._cipher_params)
self._subkey_cipher = key[subkey_size:]
# Purely for the purpose of verifying that cipher_params are OK
factory.new(key[:subkey_size], factory.MODE_ECB, **kwargs)
# Allowed transitions after initialization
self._next = [self.update, self.encrypt, self.decrypt,
self.digest, self.verify]
def _create_ctr_cipher(self, v):
"""Create a new CTR cipher from V in SIV mode"""
v_int = bytes_to_long(v)
q = v_int & 0xFFFFFFFFFFFFFFFF7FFFFFFF7FFFFFFF
return self._factory.new(
self._subkey_cipher,
self._factory.MODE_CTR,
initial_value=q,
nonce=b"",
**self._cipher_params)
def update(self, component):
"""Protect one associated data component
For SIV, the associated data is a sequence (*vector*) of non-empty
byte strings (*components*).
This method consumes the next component. It must be called
once for each of the components that constitue the associated data.
Note that the components have clear boundaries, so that:
>>> cipher.update(b"builtin")
>>> cipher.update(b"securely")
is not equivalent to:
>>> cipher.update(b"built")
>>> cipher.update(b"insecurely")
If there is no associated data, this method must not be called.
:Parameters:
component : bytes/bytearray/memoryview
The next associated data component.
"""
if self.update not in self._next:
raise TypeError("update() can only be called"
" immediately after initialization")
self._next = [self.update, self.encrypt, self.decrypt,
self.digest, self.verify]
return self._kdf.update(component)
def encrypt(self, plaintext):
"""
For SIV, encryption and MAC authentication must take place at the same
point. This method shall not be used.
Use `encrypt_and_digest` instead.
"""
raise TypeError("encrypt() not allowed for SIV mode."
" Use encrypt_and_digest() instead.")
def decrypt(self, ciphertext):
"""
For SIV, decryption and verification must take place at the same
point. This method shall not be used.
Use `decrypt_and_verify` instead.
"""
raise TypeError("decrypt() not allowed for SIV mode."
" Use decrypt_and_verify() instead.")
def digest(self):
"""Compute the *binary* MAC tag.
The caller invokes this function at the very end.
This method returns the MAC that shall be sent to the receiver,
together with the ciphertext.
:Return: the MAC, as a byte string.
"""
if self.digest not in self._next:
raise TypeError("digest() cannot be called when decrypting"
" or validating a message")
self._next = [self.digest]
if self._mac_tag is None:
self._mac_tag = self._kdf.derive()
return self._mac_tag
def hexdigest(self):
"""Compute the *printable* MAC tag.
This method is like `digest`.
:Return: the MAC, as a hexadecimal string.
"""
return "".join(["%02x" % bord(x) for x in self.digest()])
def verify(self, received_mac_tag):
"""Validate the *binary* MAC tag.
The caller invokes this function at the very end.
This method checks if the decrypted message is indeed valid
(that is, if the key is correct) and it has not been
tampered with while in transit.
:Parameters:
received_mac_tag : bytes/bytearray/memoryview
This is the *binary* MAC, as received from the sender.
:Raises ValueError:
if the MAC does not match. The message has been tampered with
or the key is incorrect.
"""
if self.verify not in self._next:
raise TypeError("verify() cannot be called"
" when encrypting a message")
self._next = [self.verify]
if self._mac_tag is None:
self._mac_tag = self._kdf.derive()
secret = get_random_bytes(16)
mac1 = BLAKE2s.new(digest_bits=160, key=secret, data=self._mac_tag)
mac2 = BLAKE2s.new(digest_bits=160, key=secret, data=received_mac_tag)
if mac1.digest() != mac2.digest():
raise ValueError("MAC check failed")
def hexverify(self, hex_mac_tag):
"""Validate the *printable* MAC tag.
This method is like `verify`.
:Parameters:
hex_mac_tag : string
This is the *printable* MAC, as received from the sender.
:Raises ValueError:
if the MAC does not match. The message has been tampered with
or the key is incorrect.
"""
self.verify(unhexlify(hex_mac_tag))
def encrypt_and_digest(self, plaintext, output=None):
"""Perform encrypt() and digest() in one step.
:Parameters:
plaintext : bytes/bytearray/memoryview
The piece of data to encrypt.
:Keywords:
output : bytearray/memoryview
The location where the ciphertext must be written to.
If ``None``, the ciphertext is returned.
:Return:
a tuple with two items:
- the ciphertext, as ``bytes``
- the MAC tag, as ``bytes``
The first item becomes ``None`` when the ``output`` parameter
specified a location for the result.
"""
if self.encrypt not in self._next:
raise TypeError("encrypt() can only be called after"
" initialization or an update()")
self._next = [ self.digest ]
# Compute V (MAC)
if hasattr(self, 'nonce'):
self._kdf.update(self.nonce)
self._kdf.update(plaintext)
self._mac_tag = self._kdf.derive()
cipher = self._create_ctr_cipher(self._mac_tag)
return cipher.encrypt(plaintext, output=output), self._mac_tag
def decrypt_and_verify(self, ciphertext, mac_tag, output=None):
"""Perform decryption and verification in one step.
A cipher object is stateful: once you have decrypted a message
you cannot decrypt (or encrypt) another message with the same
object.
You cannot reuse an object for encrypting
or decrypting other data with the same key.
This function does not remove any padding from the plaintext.
:Parameters:
ciphertext : bytes/bytearray/memoryview
The piece of data to decrypt.
It can be of any length.
mac_tag : bytes/bytearray/memoryview
This is the *binary* MAC, as received from the sender.
:Keywords:
output : bytearray/memoryview
The location where the plaintext must be written to.
If ``None``, the plaintext is returned.
:Return: the plaintext as ``bytes`` or ``None`` when the ``output``
parameter specified a location for the result.
:Raises ValueError:
if the MAC does not match. The message has been tampered with
or the key is incorrect.
"""
if self.decrypt not in self._next:
raise TypeError("decrypt() can only be called"
" after initialization or an update()")
self._next = [ self.verify ]
# Take the MAC and start the cipher for decryption
self._cipher = self._create_ctr_cipher(mac_tag)
plaintext = self._cipher.decrypt(ciphertext, output=output)
if hasattr(self, 'nonce'):
self._kdf.update(self.nonce)
self._kdf.update(plaintext if output is None else output)
self.verify(mac_tag)
return plaintext
def _create_siv_cipher(factory, **kwargs):
"""Create a new block cipher, configured in
Synthetic Initializaton Vector (SIV) mode.
:Parameters:
factory : object
A symmetric cipher module from `Crypto.Cipher`
(like `Crypto.Cipher.AES`).
:Keywords:
key : bytes/bytearray/memoryview
The secret key to use in the symmetric cipher.
It must be 32, 48 or 64 bytes long.
If AES is the chosen cipher, the variants *AES-128*,
*AES-192* and or *AES-256* will be used internally.
nonce : bytes/bytearray/memoryview
For deterministic encryption, it is not present.
Otherwise, it is a value that must never be reused
for encrypting message under this key.
There are no restrictions on its length,
but it is recommended to use at least 16 bytes.
"""
try:
key = kwargs.pop("key")
except KeyError as e:
raise TypeError("Missing parameter: " + str(e))
nonce = kwargs.pop("nonce", None)
return SivMode(factory, key, nonce, kwargs)

View File

@ -0,0 +1,247 @@
# ===================================================================
#
# Copyright (c) 2014, Legrandin <helderijs@gmail.com>
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
#
# 1. Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# 2. Redistributions in binary form must reproduce the above copyright
# notice, this list of conditions and the following disclaimer in
# the documentation and/or other materials provided with the
# distribution.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
# COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.
# ===================================================================
from binascii import unhexlify
from Crypto.Util.py3compat import bord, tobytes
from Crypto.Random import get_random_bytes
from Crypto.Util._raw_api import (load_pycryptodome_raw_lib,
VoidPointer, SmartPointer,
create_string_buffer,
get_raw_buffer, c_size_t,
c_uint8_ptr)
_raw_blake2b_lib = load_pycryptodome_raw_lib("Crypto.Hash._BLAKE2b",
"""
int blake2b_init(void **state,
const uint8_t *key,
size_t key_size,
size_t digest_size);
int blake2b_destroy(void *state);
int blake2b_update(void *state,
const uint8_t *buf,
size_t len);
int blake2b_digest(const void *state,
uint8_t digest[64]);
int blake2b_copy(const void *src, void *dst);
""")
class BLAKE2b_Hash(object):
"""A BLAKE2b hash object.
Do not instantiate directly. Use the :func:`new` function.
:ivar oid: ASN.1 Object ID
:vartype oid: string
:ivar block_size: the size in bytes of the internal message block,
input to the compression function
:vartype block_size: integer
:ivar digest_size: the size in bytes of the resulting hash
:vartype digest_size: integer
"""
# The internal block size of the hash algorithm in bytes.
block_size = 64
def __init__(self, data, key, digest_bytes, update_after_digest):
# The size of the resulting hash in bytes.
self.digest_size = digest_bytes
self._update_after_digest = update_after_digest
self._digest_done = False
# See https://tools.ietf.org/html/rfc7693
if digest_bytes in (20, 32, 48, 64) and not key:
self.oid = "1.3.6.1.4.1.1722.12.2.1." + str(digest_bytes)
state = VoidPointer()
result = _raw_blake2b_lib.blake2b_init(state.address_of(),
c_uint8_ptr(key),
c_size_t(len(key)),
c_size_t(digest_bytes)
)
if result:
raise ValueError("Error %d while instantiating BLAKE2b" % result)
self._state = SmartPointer(state.get(),
_raw_blake2b_lib.blake2b_destroy)
if data:
self.update(data)
def update(self, data):
"""Continue hashing of a message by consuming the next chunk of data.
Args:
data (bytes/bytearray/memoryview): The next chunk of the message being hashed.
"""
if self._digest_done and not self._update_after_digest:
raise TypeError("You can only call 'digest' or 'hexdigest' on this object")
result = _raw_blake2b_lib.blake2b_update(self._state.get(),
c_uint8_ptr(data),
c_size_t(len(data)))
if result:
raise ValueError("Error %d while hashing BLAKE2b data" % result)
return self
def digest(self):
"""Return the **binary** (non-printable) digest of the message that has been hashed so far.
:return: The hash digest, computed over the data processed so far.
Binary form.
:rtype: byte string
"""
bfr = create_string_buffer(64)
result = _raw_blake2b_lib.blake2b_digest(self._state.get(),
bfr)
if result:
raise ValueError("Error %d while creating BLAKE2b digest" % result)
self._digest_done = True
return get_raw_buffer(bfr)[:self.digest_size]
def hexdigest(self):
"""Return the **printable** digest of the message that has been hashed so far.
:return: The hash digest, computed over the data processed so far.
Hexadecimal encoded.
:rtype: string
"""
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.
Args:
mac_tag (bytes/bytearray/memoryview): 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.
"""
secret = get_random_bytes(16)
mac1 = new(digest_bits=160, key=secret, data=mac_tag)
mac2 = new(digest_bits=160, key=secret, data=self.digest())
if mac1.digest() != mac2.digest():
raise ValueError("MAC check failed")
def hexverify(self, hex_mac_tag):
"""Verify that a given **printable** MAC (computed by another party)
is valid.
Args:
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(self, **kwargs):
"""Return a new instance of a BLAKE2b hash object.
See :func:`new`.
"""
if "digest_bytes" not in kwargs and "digest_bits" not in kwargs:
kwargs["digest_bytes"] = self.digest_size
return new(**kwargs)
def new(**kwargs):
"""Create a new hash object.
Args:
data (bytes/bytearray/memoryview):
Optional. The very first chunk of the message to hash.
It is equivalent to an early call to :meth:`BLAKE2b_Hash.update`.
digest_bytes (integer):
Optional. The size of the digest, in bytes (1 to 64). Default is 64.
digest_bits (integer):
Optional and alternative to ``digest_bytes``.
The size of the digest, in bits (8 to 512, in steps of 8).
Default is 512.
key (bytes/bytearray/memoryview):
Optional. The key to use to compute the MAC (1 to 64 bytes).
If not specified, no key will be used.
update_after_digest (boolean):
Optional. By default, a hash object cannot be updated anymore after
the digest is computed. When this flag is ``True``, such check
is no longer enforced.
Returns:
A :class:`BLAKE2b_Hash` hash object
"""
data = kwargs.pop("data", None)
update_after_digest = kwargs.pop("update_after_digest", False)
digest_bytes = kwargs.pop("digest_bytes", None)
digest_bits = kwargs.pop("digest_bits", None)
if None not in (digest_bytes, digest_bits):
raise TypeError("Only one digest parameter must be provided")
if (None, None) == (digest_bytes, digest_bits):
digest_bytes = 64
if digest_bytes is not None:
if not (1 <= digest_bytes <= 64):
raise ValueError("'digest_bytes' not in range 1..64")
else:
if not (8 <= digest_bits <= 512) or (digest_bits % 8):
raise ValueError("'digest_bytes' not in range 8..512, "
"with steps of 8")
digest_bytes = digest_bits // 8
key = kwargs.pop("key", b"")
if len(key) > 64:
raise ValueError("BLAKE2s key cannot exceed 64 bytes")
if kwargs:
raise TypeError("Unknown parameters: " + str(kwargs))
return BLAKE2b_Hash(data, key, digest_bytes, update_after_digest)

View File

@ -0,0 +1,247 @@
# ===================================================================
#
# Copyright (c) 2014, Legrandin <helderijs@gmail.com>
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
#
# 1. Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# 2. Redistributions in binary form must reproduce the above copyright
# notice, this list of conditions and the following disclaimer in
# the documentation and/or other materials provided with the
# distribution.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
# COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.
# ===================================================================
from binascii import unhexlify
from Crypto.Util.py3compat import bord, tobytes
from Crypto.Random import get_random_bytes
from Crypto.Util._raw_api import (load_pycryptodome_raw_lib,
VoidPointer, SmartPointer,
create_string_buffer,
get_raw_buffer, c_size_t,
c_uint8_ptr)
_raw_blake2s_lib = load_pycryptodome_raw_lib("Crypto.Hash._BLAKE2s",
"""
int blake2s_init(void **state,
const uint8_t *key,
size_t key_size,
size_t digest_size);
int blake2s_destroy(void *state);
int blake2s_update(void *state,
const uint8_t *buf,
size_t len);
int blake2s_digest(const void *state,
uint8_t digest[32]);
int blake2s_copy(const void *src, void *dst);
""")
class BLAKE2s_Hash(object):
"""A BLAKE2s hash object.
Do not instantiate directly. Use the :func:`new` function.
:ivar oid: ASN.1 Object ID
:vartype oid: string
:ivar block_size: the size in bytes of the internal message block,
input to the compression function
:vartype block_size: integer
:ivar digest_size: the size in bytes of the resulting hash
:vartype digest_size: integer
"""
# The internal block size of the hash algorithm in bytes.
block_size = 32
def __init__(self, data, key, digest_bytes, update_after_digest):
# The size of the resulting hash in bytes.
self.digest_size = digest_bytes
self._update_after_digest = update_after_digest
self._digest_done = False
# See https://tools.ietf.org/html/rfc7693
if digest_bytes in (16, 20, 28, 32) and not key:
self.oid = "1.3.6.1.4.1.1722.12.2.2." + str(digest_bytes)
state = VoidPointer()
result = _raw_blake2s_lib.blake2s_init(state.address_of(),
c_uint8_ptr(key),
c_size_t(len(key)),
c_size_t(digest_bytes)
)
if result:
raise ValueError("Error %d while instantiating BLAKE2s" % result)
self._state = SmartPointer(state.get(),
_raw_blake2s_lib.blake2s_destroy)
if data:
self.update(data)
def update(self, data):
"""Continue hashing of a message by consuming the next chunk of data.
Args:
data (byte string/byte array/memoryview): The next chunk of the message being hashed.
"""
if self._digest_done and not self._update_after_digest:
raise TypeError("You can only call 'digest' or 'hexdigest' on this object")
result = _raw_blake2s_lib.blake2s_update(self._state.get(),
c_uint8_ptr(data),
c_size_t(len(data)))
if result:
raise ValueError("Error %d while hashing BLAKE2s data" % result)
return self
def digest(self):
"""Return the **binary** (non-printable) digest of the message that has been hashed so far.
:return: The hash digest, computed over the data processed so far.
Binary form.
:rtype: byte string
"""
bfr = create_string_buffer(32)
result = _raw_blake2s_lib.blake2s_digest(self._state.get(),
bfr)
if result:
raise ValueError("Error %d while creating BLAKE2s digest" % result)
self._digest_done = True
return get_raw_buffer(bfr)[:self.digest_size]
def hexdigest(self):
"""Return the **printable** digest of the message that has been hashed so far.
:return: The hash digest, computed over the data processed so far.
Hexadecimal encoded.
:rtype: string
"""
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.
Args:
mac_tag (byte string/byte array/memoryview): 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.
"""
secret = get_random_bytes(16)
mac1 = new(digest_bits=160, key=secret, data=mac_tag)
mac2 = new(digest_bits=160, key=secret, data=self.digest())
if mac1.digest() != mac2.digest():
raise ValueError("MAC check failed")
def hexverify(self, hex_mac_tag):
"""Verify that a given **printable** MAC (computed by another party)
is valid.
Args:
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(self, **kwargs):
"""Return a new instance of a BLAKE2s hash object.
See :func:`new`.
"""
if "digest_bytes" not in kwargs and "digest_bits" not in kwargs:
kwargs["digest_bytes"] = self.digest_size
return new(**kwargs)
def new(**kwargs):
"""Create a new hash object.
Args:
data (byte string/byte array/memoryview):
Optional. The very first chunk of the message to hash.
It is equivalent to an early call to :meth:`BLAKE2s_Hash.update`.
digest_bytes (integer):
Optional. The size of the digest, in bytes (1 to 32). Default is 32.
digest_bits (integer):
Optional and alternative to ``digest_bytes``.
The size of the digest, in bits (8 to 256, in steps of 8).
Default is 256.
key (byte string):
Optional. The key to use to compute the MAC (1 to 64 bytes).
If not specified, no key will be used.
update_after_digest (boolean):
Optional. By default, a hash object cannot be updated anymore after
the digest is computed. When this flag is ``True``, such check
is no longer enforced.
Returns:
A :class:`BLAKE2s_Hash` hash object
"""
data = kwargs.pop("data", None)
update_after_digest = kwargs.pop("update_after_digest", False)
digest_bytes = kwargs.pop("digest_bytes", None)
digest_bits = kwargs.pop("digest_bits", None)
if None not in (digest_bytes, digest_bits):
raise TypeError("Only one digest parameter must be provided")
if (None, None) == (digest_bytes, digest_bits):
digest_bytes = 32
if digest_bytes is not None:
if not (1 <= digest_bytes <= 32):
raise ValueError("'digest_bytes' not in range 1..32")
else:
if not (8 <= digest_bits <= 256) or (digest_bits % 8):
raise ValueError("'digest_bytes' not in range 8..256, "
"with steps of 8")
digest_bytes = digest_bits // 8
key = kwargs.pop("key", b"")
if len(key) > 32:
raise ValueError("BLAKE2s key cannot exceed 32 bytes")
if kwargs:
raise TypeError("Unknown parameters: " + str(kwargs))
return BLAKE2s_Hash(data, key, digest_bytes, update_after_digest)

View File

@ -0,0 +1,302 @@
# -*- 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.
# ===================================================================
from binascii import unhexlify
from Crypto.Hash import BLAKE2s
from Crypto.Util.strxor import strxor
from Crypto.Util.number import long_to_bytes, bytes_to_long
from Crypto.Util.py3compat import bord, tobytes, _copy_bytes
from Crypto.Random import get_random_bytes
# 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 CMAC(object):
"""A CMAC hash object.
Do not instantiate directly. Use the :func:`new` function.
:ivar digest_size: the size in bytes of the resulting MAC tag
:vartype digest_size: integer
"""
digest_size = None
def __init__(self, key, msg, ciphermod, cipher_params, mac_len,
update_after_digest):
self.digest_size = mac_len
self._key = _copy_bytes(None, None, key)
self._factory = ciphermod
self._cipher_params = cipher_params
self._block_size = bs = ciphermod.block_size
self._mac_tag = None
self._update_after_digest = update_after_digest
# Section 5.3 of NIST SP 800 38B and Appendix B
if bs == 8:
const_Rb = 0x1B
self._max_size = 8 * (2 ** 21)
elif bs == 16:
const_Rb = 0x87
self._max_size = 16 * (2 ** 48)
else:
raise TypeError("CMAC requires a cipher with a block size"
" of 8 or 16 bytes, not %d" % bs)
# Compute sub-keys
zero_block = b'\x00' * bs
self._ecb = ciphermod.new(key,
ciphermod.MODE_ECB,
**self._cipher_params)
L = self._ecb.encrypt(zero_block)
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._cbc = ciphermod.new(key,
ciphermod.MODE_CBC,
zero_block,
**self._cipher_params)
# Cache for outstanding data to authenticate
self._cache = bytearray(bs)
self._cache_n = 0
# Last piece of ciphertext produced
self._last_ct = zero_block
# Last block that was encrypted with AES
self._last_pt = None
# Counter for total message size
self._data_size = 0
if msg:
self.update(msg)
def update(self, msg):
"""Authenticate the next chunk of message.
Args:
data (byte string/byte array/memoryview): The next chunk of data
"""
if self._mac_tag is not None and not self._update_after_digest:
raise TypeError("update() cannot be called after digest() or verify()")
self._data_size += len(msg)
bs = self._block_size
if self._cache_n > 0:
filler = min(bs - self._cache_n, len(msg))
self._cache[self._cache_n:self._cache_n+filler] = msg[:filler]
self._cache_n += filler
if self._cache_n < bs:
return self
msg = memoryview(msg)[filler:]
self._update(self._cache)
self._cache_n = 0
remain = len(msg) % bs
if remain > 0:
self._update(msg[:-remain])
self._cache[:remain] = msg[-remain:]
else:
self._update(msg)
self._cache_n = remain
return self
def _update(self, data_block):
"""Update a block aligned to the block boundary"""
bs = self._block_size
assert len(data_block) % bs == 0
if len(data_block) == 0:
return
ct = self._cbc.encrypt(data_block)
if len(data_block) == bs:
second_last = self._last_ct
else:
second_last = ct[-bs*2:-bs]
self._last_ct = ct[-bs:]
self._last_pt = strxor(second_last, data_block[-bs:])
def copy(self):
"""Return a copy ("clone") of the CMAC object.
The copy will have the same internal state as the original CMAC
object.
This can be used to efficiently compute the MAC tag of byte
strings that share a common initial substring.
:return: An :class:`CMAC`
"""
obj = self.__new__(CMAC)
obj.__dict__ = self.__dict__.copy()
obj._cbc = self._factory.new(self._key,
self._factory.MODE_CBC,
self._last_ct,
**self._cipher_params)
obj._cache = self._cache[:]
obj._last_ct = self._last_ct[:]
return obj
def digest(self):
"""Return the **binary** (non-printable) MAC tag of the message
that has been authenticated so far.
:return: The MAC tag, computed over the data processed so far.
Binary form.
:rtype: byte string
"""
bs = self._block_size
if self._mac_tag is not None and not self._update_after_digest:
return self._mac_tag
if self._data_size > self._max_size:
raise ValueError("MAC is unsafe for this message")
if self._cache_n == 0 and self._data_size > 0:
# Last block was full
pt = strxor(self._last_pt, self._k1)
else:
# Last block is partial (or message length is zero)
partial = self._cache[:]
partial[self._cache_n:] = b'\x80' + b'\x00' * (bs - self._cache_n - 1)
pt = strxor(strxor(self._last_ct, partial), self._k2)
self._mac_tag = self._ecb.encrypt(pt)[:self.digest_size]
return self._mac_tag
def hexdigest(self):
"""Return the **printable** MAC tag of the message authenticated so far.
:return: The MAC tag, computed over the data processed so far.
Hexadecimal encoded.
:rtype: string
"""
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.
Args:
mac_tag (byte string/byte array/memoryview): 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.
"""
secret = get_random_bytes(16)
mac1 = BLAKE2s.new(digest_bits=160, key=secret, data=mac_tag)
mac2 = BLAKE2s.new(digest_bits=160, key=secret, data=self.digest())
if mac1.digest() != mac2.digest():
raise ValueError("MAC check failed")
def hexverify(self, hex_mac_tag):
"""Return the **printable** MAC tag of the message authenticated so far.
:return: The MAC tag, computed over the data processed so far.
Hexadecimal encoded.
:rtype: string
"""
self.verify(unhexlify(tobytes(hex_mac_tag)))
def new(key, msg=None, ciphermod=None, cipher_params=None, mac_len=None,
update_after_digest=False):
"""Create a new MAC object.
Args:
key (byte string/byte array/memoryview):
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.
ciphermod (module):
A cipher module from :mod:`Crypto.Cipher`.
The cipher's block size has to be 128 bits,
like :mod:`Crypto.Cipher.AES`, to reduce the probability
of collisions.
msg (byte string/byte array/memoryview):
Optional. The very first chunk of the message to authenticate.
It is equivalent to an early call to `CMAC.update`. Optional.
cipher_params (dict):
Optional. A set of parameters to use when instantiating a cipher
object.
mac_len (integer):
Length of the MAC, in bytes.
It must be at least 4 bytes long.
The default (and recommended) length matches the size of a cipher block.
update_after_digest (boolean):
Optional. By default, a hash object cannot be updated anymore after
the digest is computed. When this flag is ``True``, such check
is no longer enforced.
Returns:
A :class:`CMAC` object
"""
if ciphermod is None:
raise TypeError("ciphermod must be specified (try AES)")
cipher_params = {} if cipher_params is None else dict(cipher_params)
if mac_len is None:
mac_len = ciphermod.block_size
if mac_len < 4:
raise ValueError("MAC tag length must be at least 4 bytes long")
if mac_len > ciphermod.block_size:
raise ValueError("MAC tag length cannot be larger than a cipher block (%d) bytes" % ciphermod.block_size)
return CMAC(key, msg, ciphermod, cipher_params, mac_len,
update_after_digest)

View File

@ -0,0 +1,213 @@
#
# HMAC.py - Implements the HMAC algorithm as described by RFC 2104.
#
# ===================================================================
#
# Copyright (c) 2014, Legrandin <helderijs@gmail.com>
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
#
# 1. Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# 2. Redistributions in binary form must reproduce the above copyright
# notice, this list of conditions and the following disclaimer in
# the documentation and/or other materials provided with the
# distribution.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
# COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.
# ===================================================================
from Crypto.Util.py3compat import bord, tobytes
from binascii import unhexlify
from Crypto.Hash import MD5
from Crypto.Hash import BLAKE2s
from Crypto.Util.strxor import strxor
from Crypto.Random import get_random_bytes
__all__ = ['new', 'HMAC']
class HMAC(object):
"""An HMAC hash object.
Do not instantiate directly. Use the :func:`new` function.
:ivar digest_size: the size in bytes of the resulting MAC tag
:vartype digest_size: integer
"""
def __init__(self, key, msg=b"", digestmod=None):
if digestmod is None:
digestmod = MD5
if msg is None:
msg = b""
# Size of the MAC tag
self.digest_size = digestmod.digest_size
self._digestmod = digestmod
if isinstance(key, memoryview):
key = key.tobytes()
try:
if len(key) <= digestmod.block_size:
# Step 1 or 2
key_0 = key + b"\x00" * (digestmod.block_size - len(key))
else:
# Step 3
hash_k = digestmod.new(key).digest()
key_0 = hash_k + b"\x00" * (digestmod.block_size - len(hash_k))
except AttributeError:
# Not all hash types have "block_size"
raise ValueError("Hash type incompatible to HMAC")
# Step 4
key_0_ipad = strxor(key_0, b"\x36" * len(key_0))
# Start step 5 and 6
self._inner = digestmod.new(key_0_ipad)
self._inner.update(msg)
# Step 7
key_0_opad = strxor(key_0, b"\x5c" * len(key_0))
# Start step 8 and 9
self._outer = digestmod.new(key_0_opad)
def update(self, msg):
"""Authenticate the next chunk of message.
Args:
data (byte string/byte array/memoryview): The next chunk of data
"""
self._inner.update(msg)
return self
def _pbkdf2_hmac_assist(self, first_digest, iterations):
"""Carry out the expensive inner loop for PBKDF2-HMAC"""
result = self._digestmod._pbkdf2_hmac_assist(
self._inner,
self._outer,
first_digest,
iterations)
return result
def copy(self):
"""Return a copy ("clone") of the HMAC object.
The copy will have the same internal state as the original HMAC
object.
This can be used to efficiently compute the MAC tag of byte
strings that share a common initial substring.
:return: An :class:`HMAC`
"""
new_hmac = HMAC(b"fake key", digestmod=self._digestmod)
# Syncronize the state
new_hmac._inner = self._inner.copy()
new_hmac._outer = self._outer.copy()
return new_hmac
def digest(self):
"""Return the **binary** (non-printable) MAC tag of the message
authenticated so far.
:return: The MAC tag digest, computed over the data processed so far.
Binary form.
:rtype: byte string
"""
frozen_outer_hash = self._outer.copy()
frozen_outer_hash.update(self._inner.digest())
return frozen_outer_hash.digest()
def verify(self, mac_tag):
"""Verify that a given **binary** MAC (computed by another party)
is valid.
Args:
mac_tag (byte string/byte string/memoryview): 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.
"""
secret = get_random_bytes(16)
mac1 = BLAKE2s.new(digest_bits=160, key=secret, data=mac_tag)
mac2 = BLAKE2s.new(digest_bits=160, key=secret, data=self.digest())
if mac1.digest() != mac2.digest():
raise ValueError("MAC check failed")
def hexdigest(self):
"""Return the **printable** MAC tag of the message authenticated so far.
:return: The MAC tag, computed over the data processed so far.
Hexadecimal encoded.
:rtype: string
"""
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.
Args:
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=b"", digestmod=None):
"""Create a new MAC object.
Args:
key (bytes/bytearray/memoryview):
key for the MAC object.
It must be long enough to match the expected security level of the
MAC.
msg (bytes/bytearray/memoryview):
Optional. The very first chunk of the message to authenticate.
It is equivalent to an early call to :meth:`HMAC.update`.
digestmod (module):
The hash to use to implement the HMAC.
Default is :mod:`Crypto.Hash.MD5`.
Returns:
An :class:`HMAC` object
"""
return HMAC(key, msg, digestmod)

View File

@ -0,0 +1,166 @@
# ===================================================================
#
# Copyright (c) 2014, Legrandin <helderijs@gmail.com>
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
#
# 1. Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# 2. Redistributions in binary form must reproduce the above copyright
# notice, this list of conditions and the following disclaimer in
# the documentation and/or other materials provided with the
# distribution.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
# COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.
# ===================================================================
from Crypto.Util.py3compat import bord
from Crypto.Util._raw_api import (load_pycryptodome_raw_lib,
VoidPointer, SmartPointer,
create_string_buffer,
get_raw_buffer, c_size_t,
c_uint8_ptr)
_raw_md2_lib = load_pycryptodome_raw_lib(
"Crypto.Hash._MD2",
"""
int md2_init(void **shaState);
int md2_destroy(void *shaState);
int md2_update(void *hs,
const uint8_t *buf,
size_t len);
int md2_digest(const void *shaState,
uint8_t digest[20]);
int md2_copy(const void *src, void *dst);
""")
class MD2Hash(object):
"""An MD2 hash object.
Do not instantiate directly. Use the :func:`new` function.
:ivar oid: ASN.1 Object ID
:vartype oid: string
:ivar block_size: the size in bytes of the internal message block,
input to the compression function
:vartype block_size: integer
:ivar digest_size: the size in bytes of the resulting hash
:vartype digest_size: integer
"""
# The size of the resulting hash in bytes.
digest_size = 16
# The internal block size of the hash algorithm in bytes.
block_size = 16
# ASN.1 Object ID
oid = "1.2.840.113549.2.2"
def __init__(self, data=None):
state = VoidPointer()
result = _raw_md2_lib.md2_init(state.address_of())
if result:
raise ValueError("Error %d while instantiating MD2"
% result)
self._state = SmartPointer(state.get(),
_raw_md2_lib.md2_destroy)
if data:
self.update(data)
def update(self, data):
"""Continue hashing of a message by consuming the next chunk of data.
Args:
data (byte string/byte array/memoryview): The next chunk of the message being hashed.
"""
result = _raw_md2_lib.md2_update(self._state.get(),
c_uint8_ptr(data),
c_size_t(len(data)))
if result:
raise ValueError("Error %d while instantiating MD2"
% result)
def digest(self):
"""Return the **binary** (non-printable) digest of the message that has been hashed so far.
:return: The hash digest, computed over the data processed so far.
Binary form.
:rtype: byte string
"""
bfr = create_string_buffer(self.digest_size)
result = _raw_md2_lib.md2_digest(self._state.get(),
bfr)
if result:
raise ValueError("Error %d while instantiating MD2"
% result)
return get_raw_buffer(bfr)
def hexdigest(self):
"""Return the **printable** digest of the message that has been hashed so far.
:return: The hash digest, computed over the data processed so far.
Hexadecimal encoded.
:rtype: string
"""
return "".join(["%02x" % bord(x) for x in self.digest()])
def copy(self):
"""Return a copy ("clone") of the hash object.
The copy will have the same internal state as the original hash
object.
This can be used to efficiently compute the digests of strings that
share a common initial substring.
:return: A hash object of the same type
"""
clone = MD2Hash()
result = _raw_md2_lib.md2_copy(self._state.get(),
clone._state.get())
if result:
raise ValueError("Error %d while copying MD2" % result)
return clone
def new(self, data=None):
return MD2Hash(data)
def new(data=None):
"""Create a new hash object.
:parameter data:
Optional. The very first chunk of the message to hash.
It is equivalent to an early call to :meth:`MD2Hash.update`.
:type data: bytes/bytearray/memoryview
:Return: A :class:`MD2Hash` hash object
"""
return MD2Hash().new(data)
# The size of the resulting hash in bytes.
digest_size = MD2Hash.digest_size
# The internal block size of the hash algorithm in bytes.
block_size = MD2Hash.block_size

View File

@ -0,0 +1,185 @@
# ===================================================================
#
# Copyright (c) 2014, Legrandin <helderijs@gmail.com>
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
#
# 1. Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# 2. Redistributions in binary form must reproduce the above copyright
# notice, this list of conditions and the following disclaimer in
# the documentation and/or other materials provided with the
# distribution.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
# COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.
# ===================================================================
"""
MD4 is specified in RFC1320_ and produces the 128 bit digest of a message.
>>> from Crypto.Hash import MD4
>>>
>>> h = MD4.new()
>>> h.update(b'Hello')
>>> print h.hexdigest()
MD4 stand for Message Digest version 4, and it was invented by Rivest in 1990.
This algorithm is insecure. Do not use it for new designs.
.. _RFC1320: http://tools.ietf.org/html/rfc1320
"""
from Crypto.Util.py3compat import bord
from Crypto.Util._raw_api import (load_pycryptodome_raw_lib,
VoidPointer, SmartPointer,
create_string_buffer,
get_raw_buffer, c_size_t,
c_uint8_ptr)
_raw_md4_lib = load_pycryptodome_raw_lib(
"Crypto.Hash._MD4",
"""
int md4_init(void **shaState);
int md4_destroy(void *shaState);
int md4_update(void *hs,
const uint8_t *buf,
size_t len);
int md4_digest(const void *shaState,
uint8_t digest[20]);
int md4_copy(const void *src, void *dst);
""")
class MD4Hash(object):
"""Class that implements an MD4 hash
"""
#: The size of the resulting hash in bytes.
digest_size = 16
#: The internal block size of the hash algorithm in bytes.
block_size = 64
#: ASN.1 Object ID
oid = "1.2.840.113549.2.4"
def __init__(self, data=None):
state = VoidPointer()
result = _raw_md4_lib.md4_init(state.address_of())
if result:
raise ValueError("Error %d while instantiating MD4"
% result)
self._state = SmartPointer(state.get(),
_raw_md4_lib.md4_destroy)
if data:
self.update(data)
def update(self, data):
"""Continue hashing 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:
data : byte string/byte array/memoryview
The next chunk of the message being hashed.
"""
result = _raw_md4_lib.md4_update(self._state.get(),
c_uint8_ptr(data),
c_size_t(len(data)))
if result:
raise ValueError("Error %d while instantiating MD4"
% result)
def digest(self):
"""Return the **binary** (non-printable) digest of the message that
has been hashed so far.
This method does not change the state of the hash 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.
"""
bfr = create_string_buffer(self.digest_size)
result = _raw_md4_lib.md4_digest(self._state.get(),
bfr)
if result:
raise ValueError("Error %d while instantiating MD4"
% result)
return get_raw_buffer(bfr)
def hexdigest(self):
"""Return the **printable** digest of the message that has been
hashed so far.
This method does not change the state of the hash object.
:Return: A string of 2* `digest_size` characters. It contains only
hexadecimal ASCII digits.
"""
return "".join(["%02x" % bord(x) for x in self.digest()])
def copy(self):
"""Return a copy ("clone") of the hash object.
The copy will have the same internal state as the original hash
object.
This can be used to efficiently compute the digests of strings that
share a common initial substring.
:Return: A hash object of the same type
"""
clone = MD4Hash()
result = _raw_md4_lib.md4_copy(self._state.get(),
clone._state.get())
if result:
raise ValueError("Error %d while copying MD4" % result)
return clone
def new(self, data=None):
return MD4Hash(data)
def new(data=None):
"""Return a fresh instance of the hash object.
:Parameters:
data : byte string/byte array/memoryview
The very first chunk of the message to hash.
It is equivalent to an early call to `MD4Hash.update()`.
Optional.
:Return: A `MD4Hash` object
"""
return MD4Hash().new(data)
#: The size of the resulting hash in bytes.
digest_size = MD4Hash.digest_size
#: The internal block size of the hash algorithm in bytes.
block_size = MD4Hash.block_size

View File

@ -0,0 +1,184 @@
# -*- 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.
# ===================================================================
from Crypto.Util.py3compat import *
from Crypto.Util._raw_api import (load_pycryptodome_raw_lib,
VoidPointer, SmartPointer,
create_string_buffer,
get_raw_buffer, c_size_t,
c_uint8_ptr)
_raw_md5_lib = load_pycryptodome_raw_lib("Crypto.Hash._MD5",
"""
#define MD5_DIGEST_SIZE 16
int MD5_init(void **shaState);
int MD5_destroy(void *shaState);
int MD5_update(void *hs,
const uint8_t *buf,
size_t len);
int MD5_digest(const void *shaState,
uint8_t digest[MD5_DIGEST_SIZE]);
int MD5_copy(const void *src, void *dst);
int MD5_pbkdf2_hmac_assist(const void *inner,
const void *outer,
const uint8_t first_digest[MD5_DIGEST_SIZE],
uint8_t final_digest[MD5_DIGEST_SIZE],
size_t iterations);
""")
class MD5Hash(object):
"""A MD5 hash object.
Do not instantiate directly.
Use the :func:`new` function.
:ivar oid: ASN.1 Object ID
:vartype oid: string
:ivar block_size: the size in bytes of the internal message block,
input to the compression function
:vartype block_size: integer
:ivar digest_size: the size in bytes of the resulting hash
:vartype digest_size: integer
"""
# The size of the resulting hash in bytes.
digest_size = 16
# The internal block size of the hash algorithm in bytes.
block_size = 64
# ASN.1 Object ID
oid = "1.2.840.113549.2.5"
def __init__(self, data=None):
state = VoidPointer()
result = _raw_md5_lib.MD5_init(state.address_of())
if result:
raise ValueError("Error %d while instantiating MD5"
% result)
self._state = SmartPointer(state.get(),
_raw_md5_lib.MD5_destroy)
if data:
self.update(data)
def update(self, data):
"""Continue hashing of a message by consuming the next chunk of data.
Args:
data (byte string/byte array/memoryview): The next chunk of the message being hashed.
"""
result = _raw_md5_lib.MD5_update(self._state.get(),
c_uint8_ptr(data),
c_size_t(len(data)))
if result:
raise ValueError("Error %d while instantiating MD5"
% result)
def digest(self):
"""Return the **binary** (non-printable) digest of the message that has been hashed so far.
:return: The hash digest, computed over the data processed so far.
Binary form.
:rtype: byte string
"""
bfr = create_string_buffer(self.digest_size)
result = _raw_md5_lib.MD5_digest(self._state.get(),
bfr)
if result:
raise ValueError("Error %d while instantiating MD5"
% result)
return get_raw_buffer(bfr)
def hexdigest(self):
"""Return the **printable** digest of the message that has been hashed so far.
:return: The hash digest, computed over the data processed so far.
Hexadecimal encoded.
:rtype: string
"""
return "".join(["%02x" % bord(x) for x in self.digest()])
def copy(self):
"""Return a copy ("clone") of the hash object.
The copy will have the same internal state as the original hash
object.
This can be used to efficiently compute the digests of strings that
share a common initial substring.
:return: A hash object of the same type
"""
clone = MD5Hash()
result = _raw_md5_lib.MD5_copy(self._state.get(),
clone._state.get())
if result:
raise ValueError("Error %d while copying MD5" % result)
return clone
def new(self, data=None):
"""Create a fresh SHA-1 hash object."""
return MD5Hash(data)
def new(data=None):
"""Create a new hash object.
:parameter data:
Optional. The very first chunk of the message to hash.
It is equivalent to an early call to :meth:`MD5Hash.update`.
:type data: byte string/byte array/memoryview
:Return: A :class:`MD5Hash` hash object
"""
return MD5Hash().new(data)
# The size of the resulting hash in bytes.
digest_size = 16
# The internal block size of the hash algorithm in bytes.
block_size = 64
def _pbkdf2_hmac_assist(inner, outer, first_digest, iterations):
"""Compute the expensive inner loop in PBKDF-HMAC."""
assert len(first_digest) == digest_size
assert iterations > 0
bfr = create_string_buffer(digest_size);
result = _raw_md5_lib.MD5_pbkdf2_hmac_assist(
inner._state.get(),
outer._state.get(),
first_digest,
bfr,
c_size_t(iterations))
if result:
raise ValueError("Error %d with PBKDF2-HMAC assis for MD5" % result)
return get_raw_buffer(bfr)

View File

@ -0,0 +1,217 @@
# -*- coding: utf-8 -*-
#
# Hash/Poly1305.py - Implements the Poly1305 MAC
#
# ===================================================================
# 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 binascii import unhexlify
from Crypto.Util.py3compat import bord, tobytes, _copy_bytes
from Crypto.Hash import BLAKE2s
from Crypto.Random import get_random_bytes
from Crypto.Util._raw_api import (load_pycryptodome_raw_lib,
VoidPointer, SmartPointer,
create_string_buffer,
get_raw_buffer, c_size_t,
c_uint8_ptr)
_raw_poly1305 = load_pycryptodome_raw_lib("Crypto.Hash._poly1305",
"""
int poly1305_init(void **state,
const uint8_t *r,
size_t r_len,
const uint8_t *s,
size_t s_len);
int poly1305_destroy(void *state);
int poly1305_update(void *state,
const uint8_t *in,
size_t len);
int poly1305_digest(const void *state,
uint8_t *digest,
size_t len);
""")
class Poly1305_MAC(object):
"""An Poly1305 MAC object.
Do not instantiate directly. Use the :func:`new` function.
:ivar digest_size: the size in bytes of the resulting MAC tag
:vartype digest_size: integer
"""
digest_size = 16
def __init__(self, r, s, data):
if len(r) != 16:
raise ValueError("Parameter r is not 16 bytes long")
if len(s) != 16:
raise ValueError("Parameter s is not 16 bytes long")
self._mac_tag = None
state = VoidPointer()
result = _raw_poly1305.poly1305_init(state.address_of(),
c_uint8_ptr(r),
c_size_t(len(r)),
c_uint8_ptr(s),
c_size_t(len(s))
)
if result:
raise ValueError("Error %d while instantiating Poly1305" % result)
self._state = SmartPointer(state.get(),
_raw_poly1305.poly1305_destroy)
if data:
self.update(data)
def update(self, data):
"""Authenticate the next chunk of message.
Args:
data (byte string/byte array/memoryview): The next chunk of data
"""
if self._mac_tag:
raise TypeError("You can only call 'digest' or 'hexdigest' on this object")
result = _raw_poly1305.poly1305_update(self._state.get(),
c_uint8_ptr(data),
c_size_t(len(data)))
if result:
raise ValueError("Error %d while hashing Poly1305 data" % result)
return self
def copy(self):
raise NotImplementedError()
def digest(self):
"""Return the **binary** (non-printable) MAC tag of the message
authenticated so far.
:return: The MAC tag digest, computed over the data processed so far.
Binary form.
:rtype: byte string
"""
if self._mac_tag:
return self._mac_tag
bfr = create_string_buffer(16)
result = _raw_poly1305.poly1305_digest(self._state.get(),
bfr,
c_size_t(len(bfr)))
if result:
raise ValueError("Error %d while creating Poly1305 digest" % result)
self._mac_tag = get_raw_buffer(bfr)
return self._mac_tag
def hexdigest(self):
"""Return the **printable** MAC tag of the message authenticated so far.
:return: The MAC tag, computed over the data processed so far.
Hexadecimal encoded.
:rtype: string
"""
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.
Args:
mac_tag (byte string/byte string/memoryview): 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.
"""
secret = get_random_bytes(16)
mac1 = BLAKE2s.new(digest_bits=160, key=secret, data=mac_tag)
mac2 = BLAKE2s.new(digest_bits=160, key=secret, data=self.digest())
if mac1.digest() != mac2.digest():
raise ValueError("MAC check failed")
def hexverify(self, hex_mac_tag):
"""Verify that a given **printable** MAC (computed by another party)
is valid.
Args:
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(**kwargs):
"""Create a new Poly1305 MAC object.
Args:
key (bytes/bytearray/memoryview):
The 32-byte key for the Poly1305 object.
cipher (module from ``Crypto.Cipher``):
The cipher algorithm to use for deriving the Poly1305
key pair *(r, s)*.
It can only be ``Crypto.Cipher.AES`` or ``Crypto.Cipher.ChaCha20``.
nonce (bytes/bytearray/memoryview):
Optional. The non-repeatable value to use for the MAC of this message.
It must be 16 bytes long for ``AES`` and 8 or 12 bytes for ``ChaCha20``.
If not passed, a random nonce is created; you will find it in the
``nonce`` attribute of the new object.
data (bytes/bytearray/memoryview):
Optional. The very first chunk of the message to authenticate.
It is equivalent to an early call to ``update()``.
Returns:
A :class:`Poly1305_MAC` object
"""
cipher = kwargs.pop("cipher", None)
if not hasattr(cipher, '_derive_Poly1305_key_pair'):
raise ValueError("Parameter 'cipher' must be AES or ChaCha20")
cipher_key = kwargs.pop("key", None)
if cipher_key is None:
raise TypeError("You must pass a parameter 'key'")
nonce = kwargs.pop("nonce", None)
data = kwargs.pop("data", None)
if kwargs:
raise TypeError("Unknown parameters: " + str(kwargs))
r, s, nonce = cipher._derive_Poly1305_key_pair(cipher_key, nonce)
new_mac = Poly1305_MAC(r, s, data)
new_mac.nonce = _copy_bytes(None, None, nonce) # nonce may still be just a memoryview
return new_mac

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,169 @@
# ===================================================================
#
# Copyright (c) 2014, Legrandin <helderijs@gmail.com>
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
#
# 1. Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# 2. Redistributions in binary form must reproduce the above copyright
# notice, this list of conditions and the following disclaimer in
# the documentation and/or other materials provided with the
# distribution.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
# COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.
# ===================================================================
from Crypto.Util.py3compat import bord
from Crypto.Util._raw_api import (load_pycryptodome_raw_lib,
VoidPointer, SmartPointer,
create_string_buffer,
get_raw_buffer, c_size_t,
c_uint8_ptr)
_raw_ripemd160_lib = load_pycryptodome_raw_lib(
"Crypto.Hash._RIPEMD160",
"""
int ripemd160_init(void **shaState);
int ripemd160_destroy(void *shaState);
int ripemd160_update(void *hs,
const uint8_t *buf,
size_t len);
int ripemd160_digest(const void *shaState,
uint8_t digest[20]);
int ripemd160_copy(const void *src, void *dst);
""")
class RIPEMD160Hash(object):
"""A RIPEMD-160 hash object.
Do not instantiate directly.
Use the :func:`new` function.
:ivar oid: ASN.1 Object ID
:vartype oid: string
:ivar block_size: the size in bytes of the internal message block,
input to the compression function
:vartype block_size: integer
:ivar digest_size: the size in bytes of the resulting hash
:vartype digest_size: integer
"""
# The size of the resulting hash in bytes.
digest_size = 20
# The internal block size of the hash algorithm in bytes.
block_size = 64
# ASN.1 Object ID
oid = "1.3.36.3.2.1"
def __init__(self, data=None):
state = VoidPointer()
result = _raw_ripemd160_lib.ripemd160_init(state.address_of())
if result:
raise ValueError("Error %d while instantiating RIPEMD160"
% result)
self._state = SmartPointer(state.get(),
_raw_ripemd160_lib.ripemd160_destroy)
if data:
self.update(data)
def update(self, data):
"""Continue hashing of a message by consuming the next chunk of data.
Args:
data (byte string/byte array/memoryview): The next chunk of the message being hashed.
"""
result = _raw_ripemd160_lib.ripemd160_update(self._state.get(),
c_uint8_ptr(data),
c_size_t(len(data)))
if result:
raise ValueError("Error %d while instantiating ripemd160"
% result)
def digest(self):
"""Return the **binary** (non-printable) digest of the message that has been hashed so far.
:return: The hash digest, computed over the data processed so far.
Binary form.
:rtype: byte string
"""
bfr = create_string_buffer(self.digest_size)
result = _raw_ripemd160_lib.ripemd160_digest(self._state.get(),
bfr)
if result:
raise ValueError("Error %d while instantiating ripemd160"
% result)
return get_raw_buffer(bfr)
def hexdigest(self):
"""Return the **printable** digest of the message that has been hashed so far.
:return: The hash digest, computed over the data processed so far.
Hexadecimal encoded.
:rtype: string
"""
return "".join(["%02x" % bord(x) for x in self.digest()])
def copy(self):
"""Return a copy ("clone") of the hash object.
The copy will have the same internal state as the original hash
object.
This can be used to efficiently compute the digests of strings that
share a common initial substring.
:return: A hash object of the same type
"""
clone = RIPEMD160Hash()
result = _raw_ripemd160_lib.ripemd160_copy(self._state.get(),
clone._state.get())
if result:
raise ValueError("Error %d while copying ripemd160" % result)
return clone
def new(self, data=None):
"""Create a fresh RIPEMD-160 hash object."""
return RIPEMD160Hash(data)
def new(data=None):
"""Create a new hash object.
:parameter data:
Optional. The very first chunk of the message to hash.
It is equivalent to an early call to :meth:`RIPEMD160Hash.update`.
:type data: byte string/byte array/memoryview
:Return: A :class:`RIPEMD160Hash` hash object
"""
return RIPEMD160Hash().new(data)
# The size of the resulting hash in bytes.
digest_size = RIPEMD160Hash.digest_size
# The internal block size of the hash algorithm in bytes.
block_size = RIPEMD160Hash.block_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,185 @@
# -*- 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.
# ===================================================================
from Crypto.Util.py3compat import *
from Crypto.Util._raw_api import (load_pycryptodome_raw_lib,
VoidPointer, SmartPointer,
create_string_buffer,
get_raw_buffer, c_size_t,
c_uint8_ptr)
_raw_sha1_lib = load_pycryptodome_raw_lib("Crypto.Hash._SHA1",
"""
#define SHA1_DIGEST_SIZE 20
int SHA1_init(void **shaState);
int SHA1_destroy(void *shaState);
int SHA1_update(void *hs,
const uint8_t *buf,
size_t len);
int SHA1_digest(const void *shaState,
uint8_t digest[SHA1_DIGEST_SIZE]);
int SHA1_copy(const void *src, void *dst);
int SHA1_pbkdf2_hmac_assist(const void *inner,
const void *outer,
const uint8_t first_digest[SHA1_DIGEST_SIZE],
uint8_t final_digest[SHA1_DIGEST_SIZE],
size_t iterations);
""")
class SHA1Hash(object):
"""A SHA-1 hash object.
Do not instantiate directly.
Use the :func:`new` function.
:ivar oid: ASN.1 Object ID
:vartype oid: string
:ivar block_size: the size in bytes of the internal message block,
input to the compression function
:vartype block_size: integer
:ivar digest_size: the size in bytes of the resulting hash
:vartype digest_size: integer
"""
# The size of the resulting hash in bytes.
digest_size = 20
# The internal block size of the hash algorithm in bytes.
block_size = 64
# ASN.1 Object ID
oid = "1.3.14.3.2.26"
def __init__(self, data=None):
state = VoidPointer()
result = _raw_sha1_lib.SHA1_init(state.address_of())
if result:
raise ValueError("Error %d while instantiating SHA1"
% result)
self._state = SmartPointer(state.get(),
_raw_sha1_lib.SHA1_destroy)
if data:
self.update(data)
def update(self, data):
"""Continue hashing of a message by consuming the next chunk of data.
Args:
data (byte string/byte array/memoryview): The next chunk of the message being hashed.
"""
result = _raw_sha1_lib.SHA1_update(self._state.get(),
c_uint8_ptr(data),
c_size_t(len(data)))
if result:
raise ValueError("Error %d while instantiating SHA1"
% result)
def digest(self):
"""Return the **binary** (non-printable) digest of the message that has been hashed so far.
:return: The hash digest, computed over the data processed so far.
Binary form.
:rtype: byte string
"""
bfr = create_string_buffer(self.digest_size)
result = _raw_sha1_lib.SHA1_digest(self._state.get(),
bfr)
if result:
raise ValueError("Error %d while instantiating SHA1"
% result)
return get_raw_buffer(bfr)
def hexdigest(self):
"""Return the **printable** digest of the message that has been hashed so far.
:return: The hash digest, computed over the data processed so far.
Hexadecimal encoded.
:rtype: string
"""
return "".join(["%02x" % bord(x) for x in self.digest()])
def copy(self):
"""Return a copy ("clone") of the hash object.
The copy will have the same internal state as the original hash
object.
This can be used to efficiently compute the digests of strings that
share a common initial substring.
:return: A hash object of the same type
"""
clone = SHA1Hash()
result = _raw_sha1_lib.SHA1_copy(self._state.get(),
clone._state.get())
if result:
raise ValueError("Error %d while copying SHA1" % result)
return clone
def new(self, data=None):
"""Create a fresh SHA-1 hash object."""
return SHA1Hash(data)
def new(data=None):
"""Create a new hash object.
:parameter data:
Optional. The very first chunk of the message to hash.
It is equivalent to an early call to :meth:`SHA1Hash.update`.
:type data: byte string/byte array/memoryview
:Return: A :class:`SHA1Hash` hash object
"""
return SHA1Hash().new(data)
# The size of the resulting hash in bytes.
digest_size = SHA1Hash.digest_size
# The internal block size of the hash algorithm in bytes.
block_size = SHA1Hash.block_size
def _pbkdf2_hmac_assist(inner, outer, first_digest, iterations):
"""Compute the expensive inner loop in PBKDF-HMAC."""
assert len(first_digest) == digest_size
assert iterations > 0
bfr = create_string_buffer(digest_size);
result = _raw_sha1_lib.SHA1_pbkdf2_hmac_assist(
inner._state.get(),
outer._state.get(),
first_digest,
bfr,
c_size_t(iterations))
if result:
raise ValueError("Error %d with PBKDF2-HMAC assis for SHA1" % result)
return get_raw_buffer(bfr)

View File

@ -0,0 +1,186 @@
# -*- 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.
# ===================================================================
from Crypto.Util.py3compat import bord
from Crypto.Util._raw_api import (load_pycryptodome_raw_lib,
VoidPointer, SmartPointer,
create_string_buffer,
get_raw_buffer, c_size_t,
c_uint8_ptr)
_raw_sha224_lib = load_pycryptodome_raw_lib("Crypto.Hash._SHA224",
"""
int SHA224_init(void **shaState);
int SHA224_destroy(void *shaState);
int SHA224_update(void *hs,
const uint8_t *buf,
size_t len);
int SHA224_digest(const void *shaState,
uint8_t *digest,
size_t digest_size);
int SHA224_copy(const void *src, void *dst);
int SHA224_pbkdf2_hmac_assist(const void *inner,
const void *outer,
const uint8_t *first_digest,
uint8_t *final_digest,
size_t iterations,
size_t digest_size);
""")
class SHA224Hash(object):
"""A SHA-224 hash object.
Do not instantiate directly.
Use the :func:`new` function.
:ivar oid: ASN.1 Object ID
:vartype oid: string
:ivar block_size: the size in bytes of the internal message block,
input to the compression function
:vartype block_size: integer
:ivar digest_size: the size in bytes of the resulting hash
:vartype digest_size: integer
"""
# The size of the resulting hash in bytes.
digest_size = 28
# The internal block size of the hash algorithm in bytes.
block_size = 64
# ASN.1 Object ID
oid = '2.16.840.1.101.3.4.2.4'
def __init__(self, data=None):
state = VoidPointer()
result = _raw_sha224_lib.SHA224_init(state.address_of())
if result:
raise ValueError("Error %d while instantiating SHA224"
% result)
self._state = SmartPointer(state.get(),
_raw_sha224_lib.SHA224_destroy)
if data:
self.update(data)
def update(self, data):
"""Continue hashing of a message by consuming the next chunk of data.
Args:
data (byte string/byte array/memoryview): The next chunk of the message being hashed.
"""
result = _raw_sha224_lib.SHA224_update(self._state.get(),
c_uint8_ptr(data),
c_size_t(len(data)))
if result:
raise ValueError("Error %d while hashing data with SHA224"
% result)
def digest(self):
"""Return the **binary** (non-printable) digest of the message that has been hashed so far.
:return: The hash digest, computed over the data processed so far.
Binary form.
:rtype: byte string
"""
bfr = create_string_buffer(self.digest_size)
result = _raw_sha224_lib.SHA224_digest(self._state.get(),
bfr,
c_size_t(self.digest_size))
if result:
raise ValueError("Error %d while making SHA224 digest"
% result)
return get_raw_buffer(bfr)
def hexdigest(self):
"""Return the **printable** digest of the message that has been hashed so far.
:return: The hash digest, computed over the data processed so far.
Hexadecimal encoded.
:rtype: string
"""
return "".join(["%02x" % bord(x) for x in self.digest()])
def copy(self):
"""Return a copy ("clone") of the hash object.
The copy will have the same internal state as the original hash
object.
This can be used to efficiently compute the digests of strings that
share a common initial substring.
:return: A hash object of the same type
"""
clone = SHA224Hash()
result = _raw_sha224_lib.SHA224_copy(self._state.get(),
clone._state.get())
if result:
raise ValueError("Error %d while copying SHA224" % result)
return clone
def new(self, data=None):
"""Create a fresh SHA-224 hash object."""
return SHA224Hash(data)
def new(data=None):
"""Create a new hash object.
:parameter data:
Optional. The very first chunk of the message to hash.
It is equivalent to an early call to :meth:`SHA224Hash.update`.
:type data: byte string/byte array/memoryview
:Return: A :class:`SHA224Hash` hash object
"""
return SHA224Hash().new(data)
# The size of the resulting hash in bytes.
digest_size = SHA224Hash.digest_size
# The internal block size of the hash algorithm in bytes.
block_size = SHA224Hash.block_size
def _pbkdf2_hmac_assist(inner, outer, first_digest, iterations):
"""Compute the expensive inner loop in PBKDF-HMAC."""
assert iterations > 0
bfr = create_string_buffer(len(first_digest));
result = _raw_sha224_lib.SHA224_pbkdf2_hmac_assist(
inner._state.get(),
outer._state.get(),
first_digest,
bfr,
c_size_t(iterations),
c_size_t(len(first_digest)))
if result:
raise ValueError("Error %d with PBKDF2-HMAC assist for SHA224" % result)
return get_raw_buffer(bfr)

View File

@ -0,0 +1,185 @@
# -*- 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.
# ===================================================================
from Crypto.Util.py3compat import bord
from Crypto.Util._raw_api import (load_pycryptodome_raw_lib,
VoidPointer, SmartPointer,
create_string_buffer,
get_raw_buffer, c_size_t,
c_uint8_ptr)
_raw_sha256_lib = load_pycryptodome_raw_lib("Crypto.Hash._SHA256",
"""
int SHA256_init(void **shaState);
int SHA256_destroy(void *shaState);
int SHA256_update(void *hs,
const uint8_t *buf,
size_t len);
int SHA256_digest(const void *shaState,
uint8_t *digest,
size_t digest_size);
int SHA256_copy(const void *src, void *dst);
int SHA256_pbkdf2_hmac_assist(const void *inner,
const void *outer,
const uint8_t *first_digest,
uint8_t *final_digest,
size_t iterations,
size_t digest_size);
""")
class SHA256Hash(object):
"""A SHA-256 hash object.
Do not instantiate directly. Use the :func:`new` function.
:ivar oid: ASN.1 Object ID
:vartype oid: string
:ivar block_size: the size in bytes of the internal message block,
input to the compression function
:vartype block_size: integer
:ivar digest_size: the size in bytes of the resulting hash
:vartype digest_size: integer
"""
# The size of the resulting hash in bytes.
digest_size = 32
# The internal block size of the hash algorithm in bytes.
block_size = 64
# ASN.1 Object ID
oid = "2.16.840.1.101.3.4.2.1"
def __init__(self, data=None):
state = VoidPointer()
result = _raw_sha256_lib.SHA256_init(state.address_of())
if result:
raise ValueError("Error %d while instantiating SHA256"
% result)
self._state = SmartPointer(state.get(),
_raw_sha256_lib.SHA256_destroy)
if data:
self.update(data)
def update(self, data):
"""Continue hashing of a message by consuming the next chunk of data.
Args:
data (byte string/byte array/memoryview): The next chunk of the message being hashed.
"""
result = _raw_sha256_lib.SHA256_update(self._state.get(),
c_uint8_ptr(data),
c_size_t(len(data)))
if result:
raise ValueError("Error %d while hashing data with SHA256"
% result)
def digest(self):
"""Return the **binary** (non-printable) digest of the message that has been hashed so far.
:return: The hash digest, computed over the data processed so far.
Binary form.
:rtype: byte string
"""
bfr = create_string_buffer(self.digest_size)
result = _raw_sha256_lib.SHA256_digest(self._state.get(),
bfr,
c_size_t(self.digest_size))
if result:
raise ValueError("Error %d while making SHA256 digest"
% result)
return get_raw_buffer(bfr)
def hexdigest(self):
"""Return the **printable** digest of the message that has been hashed so far.
:return: The hash digest, computed over the data processed so far.
Hexadecimal encoded.
:rtype: string
"""
return "".join(["%02x" % bord(x) for x in self.digest()])
def copy(self):
"""Return a copy ("clone") of the hash object.
The copy will have the same internal state as the original hash
object.
This can be used to efficiently compute the digests of strings that
share a common initial substring.
:return: A hash object of the same type
"""
clone = SHA256Hash()
result = _raw_sha256_lib.SHA256_copy(self._state.get(),
clone._state.get())
if result:
raise ValueError("Error %d while copying SHA256" % result)
return clone
def new(self, data=None):
"""Create a fresh SHA-256 hash object."""
return SHA256Hash(data)
def new(data=None):
"""Create a new hash object.
:parameter data:
Optional. The very first chunk of the message to hash.
It is equivalent to an early call to :meth:`SHA256Hash.update`.
:type data: byte string/byte array/memoryview
:Return: A :class:`SHA256Hash` hash object
"""
return SHA256Hash().new(data)
# The size of the resulting hash in bytes.
digest_size = SHA256Hash.digest_size
# The internal block size of the hash algorithm in bytes.
block_size = SHA256Hash.block_size
def _pbkdf2_hmac_assist(inner, outer, first_digest, iterations):
"""Compute the expensive inner loop in PBKDF-HMAC."""
assert iterations > 0
bfr = create_string_buffer(len(first_digest));
result = _raw_sha256_lib.SHA256_pbkdf2_hmac_assist(
inner._state.get(),
outer._state.get(),
first_digest,
bfr,
c_size_t(iterations),
c_size_t(len(first_digest)))
if result:
raise ValueError("Error %d with PBKDF2-HMAC assist for SHA256" % result)
return get_raw_buffer(bfr)

View File

@ -0,0 +1,186 @@
# -*- 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.
# ===================================================================
from Crypto.Util.py3compat import bord
from Crypto.Util._raw_api import (load_pycryptodome_raw_lib,
VoidPointer, SmartPointer,
create_string_buffer,
get_raw_buffer, c_size_t,
c_uint8_ptr)
_raw_sha384_lib = load_pycryptodome_raw_lib("Crypto.Hash._SHA384",
"""
int SHA384_init(void **shaState);
int SHA384_destroy(void *shaState);
int SHA384_update(void *hs,
const uint8_t *buf,
size_t len);
int SHA384_digest(const void *shaState,
uint8_t *digest,
size_t digest_size);
int SHA384_copy(const void *src, void *dst);
int SHA384_pbkdf2_hmac_assist(const void *inner,
const void *outer,
const uint8_t *first_digest,
uint8_t *final_digest,
size_t iterations,
size_t digest_size);
""")
class SHA384Hash(object):
"""A SHA-384 hash object.
Do not instantiate directly. Use the :func:`new` function.
:ivar oid: ASN.1 Object ID
:vartype oid: string
:ivar block_size: the size in bytes of the internal message block,
input to the compression function
:vartype block_size: integer
:ivar digest_size: the size in bytes of the resulting hash
:vartype digest_size: integer
"""
# The size of the resulting hash in bytes.
digest_size = 48
# The internal block size of the hash algorithm in bytes.
block_size = 128
# ASN.1 Object ID
oid = '2.16.840.1.101.3.4.2.2'
def __init__(self, data=None):
state = VoidPointer()
result = _raw_sha384_lib.SHA384_init(state.address_of())
if result:
raise ValueError("Error %d while instantiating SHA384"
% result)
self._state = SmartPointer(state.get(),
_raw_sha384_lib.SHA384_destroy)
if data:
self.update(data)
def update(self, data):
"""Continue hashing of a message by consuming the next chunk of data.
Args:
data (byte string/byte array/memoryview): The next chunk of the message being hashed.
"""
result = _raw_sha384_lib.SHA384_update(self._state.get(),
c_uint8_ptr(data),
c_size_t(len(data)))
if result:
raise ValueError("Error %d while hashing data with SHA384"
% result)
def digest(self):
"""Return the **binary** (non-printable) digest of the message that has been hashed so far.
:return: The hash digest, computed over the data processed so far.
Binary form.
:rtype: byte string
"""
bfr = create_string_buffer(self.digest_size)
result = _raw_sha384_lib.SHA384_digest(self._state.get(),
bfr,
c_size_t(self.digest_size))
if result:
raise ValueError("Error %d while making SHA384 digest"
% result)
return get_raw_buffer(bfr)
def hexdigest(self):
"""Return the **printable** digest of the message that has been hashed so far.
:return: The hash digest, computed over the data processed so far.
Hexadecimal encoded.
:rtype: string
"""
return "".join(["%02x" % bord(x) for x in self.digest()])
def copy(self):
"""Return a copy ("clone") of the hash object.
The copy will have the same internal state as the original hash
object.
This can be used to efficiently compute the digests of strings that
share a common initial substring.
:return: A hash object of the same type
"""
clone = SHA384Hash()
result = _raw_sha384_lib.SHA384_copy(self._state.get(),
clone._state.get())
if result:
raise ValueError("Error %d while copying SHA384" % result)
return clone
def new(self, data=None):
"""Create a fresh SHA-384 hash object."""
return SHA384Hash(data)
def new(data=None):
"""Create a new hash object.
:parameter data:
Optional. The very first chunk of the message to hash.
It is equivalent to an early call to :meth:`SHA384Hash.update`.
:type data: byte string/byte array/memoryview
:Return: A :class:`SHA384Hash` hash object
"""
return SHA384Hash().new(data)
# The size of the resulting hash in bytes.
digest_size = SHA384Hash.digest_size
# The internal block size of the hash algorithm in bytes.
block_size = SHA384Hash.block_size
def _pbkdf2_hmac_assist(inner, outer, first_digest, iterations):
"""Compute the expensive inner loop in PBKDF-HMAC."""
assert iterations > 0
bfr = create_string_buffer(len(first_digest));
result = _raw_sha384_lib.SHA384_pbkdf2_hmac_assist(
inner._state.get(),
outer._state.get(),
first_digest,
bfr,
c_size_t(iterations),
c_size_t(len(first_digest)))
if result:
raise ValueError("Error %d with PBKDF2-HMAC assist for SHA384" % result)
return get_raw_buffer(bfr)

View File

@ -0,0 +1,171 @@
# -*- 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.
# ===================================================================
from Crypto.Util.py3compat import bord
from Crypto.Util._raw_api import (load_pycryptodome_raw_lib,
VoidPointer, SmartPointer,
create_string_buffer,
get_raw_buffer, c_size_t,
c_uint8_ptr)
from Crypto.Hash.keccak import _raw_keccak_lib
class SHA3_224_Hash(object):
"""A SHA3-224 hash object.
Do not instantiate directly.
Use the :func:`new` function.
:ivar oid: ASN.1 Object ID
:vartype oid: string
:ivar digest_size: the size in bytes of the resulting hash
:vartype digest_size: integer
"""
# The size of the resulting hash in bytes.
digest_size = 28
# ASN.1 Object ID
oid = "2.16.840.1.101.3.4.2.7"
# Input block size for HMAC
block_size = 144
def __init__(self, data, update_after_digest):
self._update_after_digest = update_after_digest
self._digest_done = False
state = VoidPointer()
result = _raw_keccak_lib.keccak_init(state.address_of(),
c_size_t(self.digest_size * 2),
0x06)
if result:
raise ValueError("Error %d while instantiating SHA-3/224"
% result)
self._state = SmartPointer(state.get(),
_raw_keccak_lib.keccak_destroy)
if data:
self.update(data)
def update(self, data):
"""Continue hashing of a message by consuming the next chunk of data.
Args:
data (byte string/byte array/memoryview): The next chunk of the message being hashed.
"""
if self._digest_done and not self._update_after_digest:
raise TypeError("You can only call 'digest' or 'hexdigest' on this object")
result = _raw_keccak_lib.keccak_absorb(self._state.get(),
c_uint8_ptr(data),
c_size_t(len(data)))
if result:
raise ValueError("Error %d while updating SHA-3/224"
% result)
return self
def digest(self):
"""Return the **binary** (non-printable) digest of the message that has been hashed so far.
:return: The hash digest, computed over the data processed so far.
Binary form.
:rtype: byte string
"""
self._digest_done = True
bfr = create_string_buffer(self.digest_size)
result = _raw_keccak_lib.keccak_digest(self._state.get(),
bfr,
c_size_t(self.digest_size))
if result:
raise ValueError("Error %d while instantiating SHA-3/224"
% result)
self._digest_value = get_raw_buffer(bfr)
return self._digest_value
def hexdigest(self):
"""Return the **printable** digest of the message that has been hashed so far.
:return: The hash digest, computed over the data processed so far.
Hexadecimal encoded.
:rtype: string
"""
return "".join(["%02x" % bord(x) for x in self.digest()])
def copy(self):
"""Return a copy ("clone") of the hash object.
The copy will have the same internal state as the original hash
object.
This can be used to efficiently compute the digests of strings that
share a common initial substring.
:return: A hash object of the same type
"""
clone = self.new()
result = _raw_keccak_lib.keccak_copy(self._state.get(),
clone._state.get())
if result:
raise ValueError("Error %d while copying SHA3-224" % result)
return clone
def new(self, data=None):
"""Create a fresh SHA3-224 hash object."""
return type(self)(data, self._update_after_digest)
def new(*args, **kwargs):
"""Create a new hash object.
Args:
data (byte string/byte array/memoryview):
The very first chunk of the message to hash.
It is equivalent to an early call to :meth:`update`.
update_after_digest (boolean):
Whether :meth:`digest` can be followed by another :meth:`update`
(default: ``False``).
:Return: A :class:`SHA3_224_Hash` hash object
"""
data = kwargs.pop("data", None)
update_after_digest = kwargs.pop("update_after_digest", False)
if len(args) == 1:
if data:
raise ValueError("Initial data for hash specified twice")
data = args[0]
if kwargs:
raise TypeError("Unknown parameters: " + str(kwargs))
return SHA3_224_Hash(data, update_after_digest)
# The size of the resulting hash in bytes.
digest_size = SHA3_224_Hash.digest_size
# Input block size for HMAC
block_size = 144

View File

@ -0,0 +1,171 @@
# -*- 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.
# ===================================================================
from Crypto.Util.py3compat import bord
from Crypto.Util._raw_api import (load_pycryptodome_raw_lib,
VoidPointer, SmartPointer,
create_string_buffer,
get_raw_buffer, c_size_t,
c_uint8_ptr)
from Crypto.Hash.keccak import _raw_keccak_lib
class SHA3_256_Hash(object):
"""A SHA3-256 hash object.
Do not instantiate directly.
Use the :func:`new` function.
:ivar oid: ASN.1 Object ID
:vartype oid: string
:ivar digest_size: the size in bytes of the resulting hash
:vartype digest_size: integer
"""
# The size of the resulting hash in bytes.
digest_size = 32
# ASN.1 Object ID
oid = "2.16.840.1.101.3.4.2.8"
# Input block size for HMAC
block_size = 136
def __init__(self, data, update_after_digest):
self._update_after_digest = update_after_digest
self._digest_done = False
state = VoidPointer()
result = _raw_keccak_lib.keccak_init(state.address_of(),
c_size_t(self.digest_size * 2),
0x06)
if result:
raise ValueError("Error %d while instantiating SHA-3/256"
% result)
self._state = SmartPointer(state.get(),
_raw_keccak_lib.keccak_destroy)
if data:
self.update(data)
def update(self, data):
"""Continue hashing of a message by consuming the next chunk of data.
Args:
data (byte string/byte array/memoryview): The next chunk of the message being hashed.
"""
if self._digest_done and not self._update_after_digest:
raise TypeError("You can only call 'digest' or 'hexdigest' on this object")
result = _raw_keccak_lib.keccak_absorb(self._state.get(),
c_uint8_ptr(data),
c_size_t(len(data)))
if result:
raise ValueError("Error %d while updating SHA-3/256"
% result)
return self
def digest(self):
"""Return the **binary** (non-printable) digest of the message that has been hashed so far.
:return: The hash digest, computed over the data processed so far.
Binary form.
:rtype: byte string
"""
self._digest_done = True
bfr = create_string_buffer(self.digest_size)
result = _raw_keccak_lib.keccak_digest(self._state.get(),
bfr,
c_size_t(self.digest_size))
if result:
raise ValueError("Error %d while instantiating SHA-3/256"
% result)
self._digest_value = get_raw_buffer(bfr)
return self._digest_value
def hexdigest(self):
"""Return the **printable** digest of the message that has been hashed so far.
:return: The hash digest, computed over the data processed so far.
Hexadecimal encoded.
:rtype: string
"""
return "".join(["%02x" % bord(x) for x in self.digest()])
def copy(self):
"""Return a copy ("clone") of the hash object.
The copy will have the same internal state as the original hash
object.
This can be used to efficiently compute the digests of strings that
share a common initial substring.
:return: A hash object of the same type
"""
clone = self.new()
result = _raw_keccak_lib.keccak_copy(self._state.get(),
clone._state.get())
if result:
raise ValueError("Error %d while copying SHA3-256" % result)
return clone
def new(self, data=None):
"""Create a fresh SHA3-256 hash object."""
return type(self)(data, self._update_after_digest)
def new(*args, **kwargs):
"""Create a new hash object.
Args:
data (byte string/byte array/memoryview):
The very first chunk of the message to hash.
It is equivalent to an early call to :meth:`update`.
update_after_digest (boolean):
Whether :meth:`digest` can be followed by another :meth:`update`
(default: ``False``).
:Return: A :class:`SHA3_256_Hash` hash object
"""
data = kwargs.pop("data", None)
update_after_digest = kwargs.pop("update_after_digest", False)
if len(args) == 1:
if data:
raise ValueError("Initial data for hash specified twice")
data = args[0]
if kwargs:
raise TypeError("Unknown parameters: " + str(kwargs))
return SHA3_256_Hash(data, update_after_digest)
# The size of the resulting hash in bytes.
digest_size = SHA3_256_Hash.digest_size
# Input block size for HMAC
block_size = 136

View File

@ -0,0 +1,177 @@
# -*- 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.
# ===================================================================
from Crypto.Util.py3compat import bord
from Crypto.Util._raw_api import (load_pycryptodome_raw_lib,
VoidPointer, SmartPointer,
create_string_buffer,
get_raw_buffer, c_size_t,
c_uint8_ptr)
from Crypto.Hash.keccak import _raw_keccak_lib
class SHA3_384_Hash(object):
"""A SHA3-384 hash object.
Do not instantiate directly.
Use the :func:`new` function.
:ivar oid: ASN.1 Object ID
:vartype oid: string
:ivar digest_size: the size in bytes of the resulting hash
:vartype digest_size: integer
"""
# The size of the resulting hash in bytes.
digest_size = 48
# ASN.1 Object ID
oid = "2.16.840.1.101.3.4.2.9"
# Input block size for HMAC
block_size = 104
def __init__(self, data, update_after_digest):
self._update_after_digest = update_after_digest
self._digest_done = False
state = VoidPointer()
result = _raw_keccak_lib.keccak_init(state.address_of(),
c_size_t(self.digest_size * 2),
0x06)
if result:
raise ValueError("Error %d while instantiating SHA-3/384"
% result)
self._state = SmartPointer(state.get(),
_raw_keccak_lib.keccak_destroy)
if data:
self.update(data)
def update(self, data):
"""Continue hashing of a message by consuming the next chunk of data.
Args:
data (byte string/byte array/memoryview): The next chunk of the message being hashed.
"""
if self._digest_done and not self._update_after_digest:
raise TypeError("You can only call 'digest' or 'hexdigest' on this object")
result = _raw_keccak_lib.keccak_absorb(self._state.get(),
c_uint8_ptr(data),
c_size_t(len(data)))
if result:
raise ValueError("Error %d while updating SHA-3/384"
% result)
return self
def digest(self):
"""Return the **binary** (non-printable) digest of the message that has been hashed so far.
:return: The hash digest, computed over the data processed so far.
Binary form.
:rtype: byte string
"""
self._digest_done = True
bfr = create_string_buffer(self.digest_size)
result = _raw_keccak_lib.keccak_digest(self._state.get(),
bfr,
c_size_t(self.digest_size))
if result:
raise ValueError("Error %d while instantiating SHA-3/384"
% result)
self._digest_value = get_raw_buffer(bfr)
return self._digest_value
def hexdigest(self):
"""Return the **printable** digest of the message that has been hashed so far.
:return: The hash digest, computed over the data processed so far.
Hexadecimal encoded.
:rtype: string
"""
return "".join(["%02x" % bord(x) for x in self.digest()])
def copy(self):
"""Return a copy ("clone") of the hash object.
The copy will have the same internal state as the original hash
object.
This can be used to efficiently compute the digests of strings that
share a common initial substring.
:return: A hash object of the same type
"""
clone = self.new()
result = _raw_keccak_lib.keccak_copy(self._state.get(),
clone._state.get())
if result:
raise ValueError("Error %d while copying SHA3-384" % result)
return clone
def new(self, data=None):
"""Create a fresh SHA3-256 hash object."""
return type(self)(data, self._update_after_digest)
def new(self, data=None):
"""Create a fresh SHA3-384 hash object."""
return type(self)(data, self._update_after_digest)
def new(*args, **kwargs):
"""Create a new hash object.
Args:
data (byte string/byte array/memoryview):
The very first chunk of the message to hash.
It is equivalent to an early call to :meth:`update`.
update_after_digest (boolean):
Whether :meth:`digest` can be followed by another :meth:`update`
(default: ``False``).
:Return: A :class:`SHA3_384_Hash` hash object
"""
data = kwargs.pop("data", None)
update_after_digest = kwargs.pop("update_after_digest", False)
if len(args) == 1:
if data:
raise ValueError("Initial data for hash specified twice")
data = args[0]
if kwargs:
raise TypeError("Unknown parameters: " + str(kwargs))
return SHA3_384_Hash(data, update_after_digest)
# The size of the resulting hash in bytes.
digest_size = SHA3_384_Hash.digest_size
# Input block size for HMAC
block_size = 104

View File

@ -0,0 +1,172 @@
# -*- 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.
# ===================================================================
from Crypto.Util.py3compat import bord
from Crypto.Util._raw_api import (load_pycryptodome_raw_lib,
VoidPointer, SmartPointer,
create_string_buffer,
get_raw_buffer, c_size_t,
c_uint8_ptr)
from Crypto.Hash.keccak import _raw_keccak_lib
class SHA3_512_Hash(object):
"""A SHA3-512 hash object.
Do not instantiate directly.
Use the :func:`new` function.
:ivar oid: ASN.1 Object ID
:vartype oid: string
:ivar digest_size: the size in bytes of the resulting hash
:vartype digest_size: integer
"""
# The size of the resulting hash in bytes.
digest_size = 64
# ASN.1 Object ID
oid = "2.16.840.1.101.3.4.2.10"
# Input block size for HMAC
block_size = 72
def __init__(self, data, update_after_digest):
self._update_after_digest = update_after_digest
self._digest_done = False
state = VoidPointer()
result = _raw_keccak_lib.keccak_init(state.address_of(),
c_size_t(self.digest_size * 2),
0x06)
if result:
raise ValueError("Error %d while instantiating SHA-3/512"
% result)
self._state = SmartPointer(state.get(),
_raw_keccak_lib.keccak_destroy)
if data:
self.update(data)
def update(self, data):
"""Continue hashing of a message by consuming the next chunk of data.
Args:
data (byte string/byte array/memoryview): The next chunk of the message being hashed.
"""
if self._digest_done and not self._update_after_digest:
raise TypeError("You can only call 'digest' or 'hexdigest' on this object")
result = _raw_keccak_lib.keccak_absorb(self._state.get(),
c_uint8_ptr(data),
c_size_t(len(data)))
if result:
raise ValueError("Error %d while updating SHA-3/512"
% result)
return self
def digest(self):
"""Return the **binary** (non-printable) digest of the message that has been hashed so far.
:return: The hash digest, computed over the data processed so far.
Binary form.
:rtype: byte string
"""
self._digest_done = True
bfr = create_string_buffer(self.digest_size)
result = _raw_keccak_lib.keccak_digest(self._state.get(),
bfr,
c_size_t(self.digest_size))
if result:
raise ValueError("Error %d while instantiating SHA-3/512"
% result)
self._digest_value = get_raw_buffer(bfr)
return self._digest_value
def hexdigest(self):
"""Return the **printable** digest of the message that has been hashed so far.
:return: The hash digest, computed over the data processed so far.
Hexadecimal encoded.
:rtype: string
"""
return "".join(["%02x" % bord(x) for x in self.digest()])
def copy(self):
"""Return a copy ("clone") of the hash object.
The copy will have the same internal state as the original hash
object.
This can be used to efficiently compute the digests of strings that
share a common initial substring.
:return: A hash object of the same type
"""
clone = self.new()
result = _raw_keccak_lib.keccak_copy(self._state.get(),
clone._state.get())
if result:
raise ValueError("Error %d while copying SHA3-512" % result)
return clone
def new(self, data=None):
"""Create a fresh SHA3-521 hash object."""
return type(self)(data, self._update_after_digest)
def new(*args, **kwargs):
"""Create a new hash object.
Args:
data (byte string/byte array/memoryview):
The very first chunk of the message to hash.
It is equivalent to an early call to :meth:`update`.
update_after_digest (boolean):
Whether :meth:`digest` can be followed by another :meth:`update`
(default: ``False``).
:Return: A :class:`SHA3_512_Hash` hash object
"""
data = kwargs.pop("data", None)
update_after_digest = kwargs.pop("update_after_digest", False)
if len(args) == 1:
if data:
raise ValueError("Initial data for hash specified twice")
data = args[0]
if kwargs:
raise TypeError("Unknown parameters: " + str(kwargs))
return SHA3_512_Hash(data, update_after_digest)
# The size of the resulting hash in bytes.
digest_size = SHA3_512_Hash.digest_size
# Input block size for HMAC
block_size = 72

View File

@ -0,0 +1,204 @@
# -*- 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.
# ===================================================================
from Crypto.Util.py3compat import bord
from Crypto.Util._raw_api import (load_pycryptodome_raw_lib,
VoidPointer, SmartPointer,
create_string_buffer,
get_raw_buffer, c_size_t,
c_uint8_ptr)
_raw_sha512_lib = load_pycryptodome_raw_lib("Crypto.Hash._SHA512",
"""
int SHA512_init(void **shaState,
size_t digest_size);
int SHA512_destroy(void *shaState);
int SHA512_update(void *hs,
const uint8_t *buf,
size_t len);
int SHA512_digest(const void *shaState,
uint8_t *digest,
size_t digest_size);
int SHA512_copy(const void *src, void *dst);
int SHA512_pbkdf2_hmac_assist(const void *inner,
const void *outer,
const uint8_t *first_digest,
uint8_t *final_digest,
size_t iterations,
size_t digest_size);
""")
class SHA512Hash(object):
"""A SHA-512 hash object (possibly in its truncated version SHA-512/224 or
SHA-512/256.
Do not instantiate directly. Use the :func:`new` function.
:ivar oid: ASN.1 Object ID
:vartype oid: string
:ivar block_size: the size in bytes of the internal message block,
input to the compression function
:vartype block_size: integer
:ivar digest_size: the size in bytes of the resulting hash
:vartype digest_size: integer
"""
# The internal block size of the hash algorithm in bytes.
block_size = 128
def __init__(self, data, truncate):
self._truncate = truncate
if truncate is None:
self.oid = "2.16.840.1.101.3.4.2.3"
self.digest_size = 64
elif truncate == "224":
self.oid = "2.16.840.1.101.3.4.2.5"
self.digest_size = 28
elif truncate == "256":
self.oid = "2.16.840.1.101.3.4.2.6"
self.digest_size = 32
else:
raise ValueError("Incorrect truncation length. It must be '224' or '256'.")
state = VoidPointer()
result = _raw_sha512_lib.SHA512_init(state.address_of(),
c_size_t(self.digest_size))
if result:
raise ValueError("Error %d while instantiating SHA-512"
% result)
self._state = SmartPointer(state.get(),
_raw_sha512_lib.SHA512_destroy)
if data:
self.update(data)
def update(self, data):
"""Continue hashing of a message by consuming the next chunk of data.
Args:
data (byte string/byte array/memoryview): The next chunk of the message being hashed.
"""
result = _raw_sha512_lib.SHA512_update(self._state.get(),
c_uint8_ptr(data),
c_size_t(len(data)))
if result:
raise ValueError("Error %d while hashing data with SHA512"
% result)
def digest(self):
"""Return the **binary** (non-printable) digest of the message that has been hashed so far.
:return: The hash digest, computed over the data processed so far.
Binary form.
:rtype: byte string
"""
bfr = create_string_buffer(self.digest_size)
result = _raw_sha512_lib.SHA512_digest(self._state.get(),
bfr,
c_size_t(self.digest_size))
if result:
raise ValueError("Error %d while making SHA512 digest"
% result)
return get_raw_buffer(bfr)
def hexdigest(self):
"""Return the **printable** digest of the message that has been hashed so far.
:return: The hash digest, computed over the data processed so far.
Hexadecimal encoded.
:rtype: string
"""
return "".join(["%02x" % bord(x) for x in self.digest()])
def copy(self):
"""Return a copy ("clone") of the hash object.
The copy will have the same internal state as the original hash
object.
This can be used to efficiently compute the digests of strings that
share a common initial substring.
:return: A hash object of the same type
"""
clone = SHA512Hash(None, self._truncate)
result = _raw_sha512_lib.SHA512_copy(self._state.get(),
clone._state.get())
if result:
raise ValueError("Error %d while copying SHA512" % result)
return clone
def new(self, data=None):
"""Create a fresh SHA-512 hash object."""
return SHA512Hash(data, self._truncate)
def new(data=None, truncate=None):
"""Create a new hash object.
Args:
data (bytes/bytearray/memoryview):
Optional. The very first chunk of the message to hash.
It is equivalent to an early call to :meth:`SHA512Hash.update`.
truncate (string):
Optional. The desired length of the digest. It can be either "224" or
"256". If not present, the digest is 512 bits long.
Passing this parameter is **not** equivalent to simply truncating
the output digest.
:Return: A :class:`SHA512Hash` hash object
"""
return SHA512Hash(data, truncate)
# The size of the full SHA-512 hash in bytes.
digest_size = 64
# The internal block size of the hash algorithm in bytes.
block_size = 128
def _pbkdf2_hmac_assist(inner, outer, first_digest, iterations):
"""Compute the expensive inner loop in PBKDF-HMAC."""
assert iterations > 0
bfr = create_string_buffer(len(first_digest));
result = _raw_sha512_lib.SHA512_pbkdf2_hmac_assist(
inner._state.get(),
outer._state.get(),
first_digest,
bfr,
c_size_t(iterations),
c_size_t(len(first_digest)))
if result:
raise ValueError("Error %d with PBKDF2-HMAC assist for SHA512" % result)
return get_raw_buffer(bfr)

View File

@ -0,0 +1,127 @@
# ===================================================================
#
# Copyright (c) 2015, Legrandin <helderijs@gmail.com>
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
#
# 1. Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# 2. Redistributions in binary form must reproduce the above copyright
# notice, this list of conditions and the following disclaimer in
# the documentation and/or other materials provided with the
# distribution.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
# COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.
# ===================================================================
from Crypto.Util.py3compat import bord
from Crypto.Util._raw_api import (load_pycryptodome_raw_lib,
VoidPointer, SmartPointer,
create_string_buffer,
get_raw_buffer, c_size_t,
c_uint8_ptr)
from Crypto.Hash.keccak import _raw_keccak_lib
class SHAKE128_XOF(object):
"""A SHAKE128 hash object.
Do not instantiate directly.
Use the :func:`new` function.
:ivar oid: ASN.1 Object ID
:vartype oid: string
"""
# ASN.1 Object ID
oid = "2.16.840.1.101.3.4.2.11"
def __init__(self, data=None):
state = VoidPointer()
result = _raw_keccak_lib.keccak_init(state.address_of(),
c_size_t(32),
0x1F)
if result:
raise ValueError("Error %d while instantiating SHAKE128"
% result)
self._state = SmartPointer(state.get(),
_raw_keccak_lib.keccak_destroy)
self._is_squeezing = False
if data:
self.update(data)
def update(self, data):
"""Continue hashing of a message by consuming the next chunk of data.
Args:
data (byte string/byte array/memoryview): The next chunk of the message being hashed.
"""
if self._is_squeezing:
raise TypeError("You cannot call 'update' after the first 'read'")
result = _raw_keccak_lib.keccak_absorb(self._state.get(),
c_uint8_ptr(data),
c_size_t(len(data)))
if result:
raise ValueError("Error %d while updating SHAKE128 state"
% result)
return self
def read(self, length):
"""
Compute the next piece of XOF output.
.. note::
You cannot use :meth:`update` anymore after the first call to
:meth:`read`.
Args:
length (integer): the amount of bytes this method must return
:return: the next piece of XOF output (of the given length)
:rtype: byte string
"""
self._is_squeezing = True
bfr = create_string_buffer(length)
result = _raw_keccak_lib.keccak_squeeze(self._state.get(),
bfr,
c_size_t(length))
if result:
raise ValueError("Error %d while extracting from SHAKE128"
% result)
return get_raw_buffer(bfr)
def new(self, data=None):
return type(self)(data=data)
def new(data=None):
"""Return a fresh instance of a SHAKE128 object.
Args:
data (bytes/bytearray/memoryview):
The very first chunk of the message to hash.
It is equivalent to an early call to :meth:`update`.
Optional.
:Return: A :class:`SHAKE128_XOF` object
"""
return SHAKE128_XOF(data=data)

View File

@ -0,0 +1,127 @@
# ===================================================================
#
# Copyright (c) 2015, Legrandin <helderijs@gmail.com>
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
#
# 1. Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# 2. Redistributions in binary form must reproduce the above copyright
# notice, this list of conditions and the following disclaimer in
# the documentation and/or other materials provided with the
# distribution.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
# COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.
# ===================================================================
from Crypto.Util.py3compat import bord
from Crypto.Util._raw_api import (load_pycryptodome_raw_lib,
VoidPointer, SmartPointer,
create_string_buffer,
get_raw_buffer, c_size_t,
c_uint8_ptr)
from Crypto.Hash.keccak import _raw_keccak_lib
class SHAKE256_XOF(object):
"""A SHAKE256 hash object.
Do not instantiate directly.
Use the :func:`new` function.
:ivar oid: ASN.1 Object ID
:vartype oid: string
"""
# ASN.1 Object ID
oid = "2.16.840.1.101.3.4.2.12"
def __init__(self, data=None):
state = VoidPointer()
result = _raw_keccak_lib.keccak_init(state.address_of(),
c_size_t(64),
0x1F)
if result:
raise ValueError("Error %d while instantiating SHAKE256"
% result)
self._state = SmartPointer(state.get(),
_raw_keccak_lib.keccak_destroy)
self._is_squeezing = False
if data:
self.update(data)
def update(self, data):
"""Continue hashing of a message by consuming the next chunk of data.
Args:
data (byte string/byte array/memoryview): The next chunk of the message being hashed.
"""
if self._is_squeezing:
raise TypeError("You cannot call 'update' after the first 'read'")
result = _raw_keccak_lib.keccak_absorb(self._state.get(),
c_uint8_ptr(data),
c_size_t(len(data)))
if result:
raise ValueError("Error %d while updating SHAKE256 state"
% result)
return self
def read(self, length):
"""
Compute the next piece of XOF output.
.. note::
You cannot use :meth:`update` anymore after the first call to
:meth:`read`.
Args:
length (integer): the amount of bytes this method must return
:return: the next piece of XOF output (of the given length)
:rtype: byte string
"""
self._is_squeezing = True
bfr = create_string_buffer(length)
result = _raw_keccak_lib.keccak_squeeze(self._state.get(),
bfr,
c_size_t(length))
if result:
raise ValueError("Error %d while extracting from SHAKE256"
% result)
return get_raw_buffer(bfr)
def new(self, data=None):
return type(self)(data=data)
def new(data=None):
"""Return a fresh instance of a SHAKE256 object.
Args:
data (bytes/bytearray/memoryview):
The very first chunk of the message to hash.
It is equivalent to an early call to :meth:`update`.
Optional.
:Return: A :class:`SHAKE256_XOF` object
"""
return SHAKE256_XOF(data=data)

View File

@ -0,0 +1,22 @@
# -*- 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.
# ===================================================================
__all__ = ['HMAC', 'MD2', 'MD4', 'MD5', 'RIPEMD160', 'SHA1',
'SHA224', 'SHA256', 'SHA384', 'SHA512', 'CMAC', 'Poly1305']

View File

@ -0,0 +1,174 @@
# ===================================================================
#
# Copyright (c) 2015, Legrandin <helderijs@gmail.com>
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
#
# 1. Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# 2. Redistributions in binary form must reproduce the above copyright
# notice, this list of conditions and the following disclaimer in
# the documentation and/or other materials provided with the
# distribution.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
# COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.
# ===================================================================
from Crypto.Util.py3compat import bord
from Crypto.Util._raw_api import (load_pycryptodome_raw_lib,
VoidPointer, SmartPointer,
create_string_buffer,
get_raw_buffer, c_size_t,
c_uint8_ptr)
_raw_keccak_lib = load_pycryptodome_raw_lib("Crypto.Hash._keccak",
"""
int keccak_init(void **state,
size_t capacity_bytes,
uint8_t padding_byte);
int keccak_destroy(void *state);
int keccak_absorb(void *state,
const uint8_t *in,
size_t len);
int keccak_squeeze(const void *state,
uint8_t *out,
size_t len);
int keccak_digest(void *state, uint8_t *digest, size_t len);
int keccak_copy(const void *src, void *dst);
""")
class Keccak_Hash(object):
"""A Keccak hash object.
Do not instantiate directly.
Use the :func:`new` function.
:ivar digest_size: the size in bytes of the resulting hash
:vartype digest_size: integer
"""
def __init__(self, data, digest_bytes, update_after_digest):
# The size of the resulting hash in bytes.
self.digest_size = digest_bytes
self._update_after_digest = update_after_digest
self._digest_done = False
state = VoidPointer()
result = _raw_keccak_lib.keccak_init(state.address_of(),
c_size_t(self.digest_size * 2),
0x01)
if result:
raise ValueError("Error %d while instantiating keccak" % result)
self._state = SmartPointer(state.get(),
_raw_keccak_lib.keccak_destroy)
if data:
self.update(data)
def update(self, data):
"""Continue hashing of a message by consuming the next chunk of data.
Args:
data (byte string/byte array/memoryview): The next chunk of the message being hashed.
"""
if self._digest_done and not self._update_after_digest:
raise TypeError("You can only call 'digest' or 'hexdigest' on this object")
result = _raw_keccak_lib.keccak_absorb(self._state.get(),
c_uint8_ptr(data),
c_size_t(len(data)))
if result:
raise ValueError("Error %d while updating keccak" % result)
return self
def digest(self):
"""Return the **binary** (non-printable) digest of the message that has been hashed so far.
:return: The hash digest, computed over the data processed so far.
Binary form.
:rtype: byte string
"""
self._digest_done = True
bfr = create_string_buffer(self.digest_size)
result = _raw_keccak_lib.keccak_digest(self._state.get(),
bfr,
c_size_t(self.digest_size))
if result:
raise ValueError("Error %d while squeezing keccak" % result)
return get_raw_buffer(bfr)
def hexdigest(self):
"""Return the **printable** digest of the message that has been hashed so far.
:return: The hash digest, computed over the data processed so far.
Hexadecimal encoded.
:rtype: string
"""
return "".join(["%02x" % bord(x) for x in self.digest()])
def new(self, **kwargs):
"""Create a fresh Keccak hash object."""
if "digest_bytes" not in kwargs and "digest_bits" not in kwargs:
kwargs["digest_bytes"] = self.digest_size
return new(**kwargs)
def new(**kwargs):
"""Create a new hash object.
Args:
data (bytes/bytearray/memoryview):
The very first chunk of the message to hash.
It is equivalent to an early call to :meth:`Keccak_Hash.update`.
digest_bytes (integer):
The size of the digest, in bytes (28, 32, 48, 64).
digest_bits (integer):
The size of the digest, in bits (224, 256, 384, 512).
update_after_digest (boolean):
Whether :meth:`Keccak.digest` can be followed by another
:meth:`Keccak.update` (default: ``False``).
:Return: A :class:`Keccak_Hash` hash object
"""
data = kwargs.pop("data", None)
update_after_digest = kwargs.pop("update_after_digest", False)
digest_bytes = kwargs.pop("digest_bytes", None)
digest_bits = kwargs.pop("digest_bits", None)
if None not in (digest_bytes, digest_bits):
raise TypeError("Only one digest parameter must be provided")
if (None, None) == (digest_bytes, digest_bits):
raise TypeError("Digest size (bits, bytes) not provided")
if digest_bytes is not None:
if digest_bytes not in (28, 32, 48, 64):
raise ValueError("'digest_bytes' must be: 28, 32, 48 or 64")
else:
if digest_bits not in (224, 256, 384, 512):
raise ValueError("'digest_bytes' must be: 224, 256, 384 or 512")
digest_bytes = digest_bits // 8
if kwargs:
raise TypeError("Unknown parameters: " + str(kwargs))
return Keccak_Hash(data, digest_bytes, update_after_digest)

View File

@ -0,0 +1,189 @@
#
# Util/PEM.py : Privacy Enhanced Mail utilities
#
# ===================================================================
#
# Copyright (c) 2014, Legrandin <helderijs@gmail.com>
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
#
# 1. Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# 2. Redistributions in binary form must reproduce the above copyright
# notice, this list of conditions and the following disclaimer in
# the documentation and/or other materials provided with the
# distribution.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
# COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.
# ===================================================================
__all__ = ['encode', 'decode']
import re
from binascii import a2b_base64, b2a_base64, hexlify, unhexlify
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
from Crypto.Util.py3compat import tobytes, tostr
def encode(data, marker, passphrase=None, randfunc=None):
"""Encode a piece of binary data into PEM format.
Args:
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: https://github.com/openssl/openssl/blob/master/include/openssl/pem.h
"""
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))
elif passphrase is not None:
raise ValueError("Empty password")
# 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 _EVP_BytesToKey(data, salt, key_len):
d = [ b'' ]
m = (key_len + 15 ) // 16
for _ in range(m):
nd = MD5.new(d[-1] + data + salt).digest()
d.append(nd)
return b"".join(d)[:key_len]
def decode(pem_data, passphrase=None):
"""Decode a PEM block into binary.
Args:
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(r"\s*-----BEGIN (.*)-----\s+")
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(r"-----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))
padding = True
if algo == "DES-CBC":
key = _EVP_BytesToKey(passphrase, salt, 8)
objdec = DES.new(key, DES.MODE_CBC, salt)
elif algo == "DES-EDE3-CBC":
key = _EVP_BytesToKey(passphrase, salt, 24)
objdec = DES3.new(key, DES3.MODE_CBC, salt)
elif algo == "AES-128-CBC":
key = _EVP_BytesToKey(passphrase, salt[:8], 16)
objdec = AES.new(key, AES.MODE_CBC, salt)
elif algo == "AES-192-CBC":
key = _EVP_BytesToKey(passphrase, salt[:8], 24)
objdec = AES.new(key, AES.MODE_CBC, salt)
elif algo == "AES-256-CBC":
key = _EVP_BytesToKey(passphrase, salt[:8], 32)
objdec = AES.new(key, AES.MODE_CBC, salt)
elif algo.lower() == "id-aes256-gcm":
key = _EVP_BytesToKey(passphrase, salt[:8], 32)
objdec = AES.new(key, AES.MODE_GCM, nonce=salt)
padding = False
else:
raise ValueError("Unsupport PEM encryption algorithm (%s)." % algo)
lines = lines[2:]
else:
objdec = None
# Decode body
data = a2b_base64(''.join(lines[1:-1]))
enc_flag = False
if objdec:
if padding:
data = unpad(objdec.decrypt(data), objdec.block_size)
else:
# There is no tag, so we don't use decrypt_and_verify
data = objdec.decrypt(data)
enc_flag = True
return (data, marker, enc_flag)

View File

@ -0,0 +1,231 @@
#
# PublicKey/PKCS8.py : PKCS#8 functions
#
# ===================================================================
#
# Copyright (c) 2014, Legrandin <helderijs@gmail.com>
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
#
# 1. Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# 2. Redistributions in binary form must reproduce the above copyright
# notice, this list of conditions and the following disclaimer in
# the documentation and/or other materials provided with the
# distribution.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
# COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.
# ===================================================================
from Crypto.Util.py3compat import *
from Crypto.Util.asn1 import (
DerNull,
DerSequence,
DerObjectId,
DerOctetString,
)
from Crypto.IO._PBES import PBES1, PBES2, PbesError
__all__ = ['wrap', 'unwrap']
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).
Args:
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 (bytes string or 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 |
| | (called *N* or CPU/memory cost in scrypt). |
| | The default value for PBKDF2 is 1000. |
| | The default value for scrypt is 16384. |
+------------------+-----------------------------------------------+
| salt_size | Salt is used to thwart dictionary and rainbow |
| | attacks on passwords. The default value is 8 |
| | bytes. |
+------------------+-----------------------------------------------+
| block_size | *(scrypt only)* Memory-cost (r). The default |
| | value is 8. |
+------------------+-----------------------------------------------+
| parallelization | *(scrypt only)* CPU-cost (p). The default |
| | value is 1. |
+------------------+-----------------------------------------------+
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 :mod:`Crypto.Random`.
Return:
The PKCS#8-wrapped private key (possibly encrypted), as a byte string.
"""
if key_params is None:
key_params = DerNull()
#
# PrivateKeyInfo ::= SEQUENCE {
# version Version,
# privateKeyAlgorithm PrivateKeyAlgorithmIdentifier,
# privateKey PrivateKey,
# attributes [0] IMPLICIT Attributes OPTIONAL
# }
#
pk_info = DerSequence([
0,
DerSequence([
DerObjectId(key_oid),
key_params
]),
DerOctetString(private_key)
])
pk_info_der = pk_info.encode()
if passphrase is None:
return pk_info_der
if not passphrase:
raise ValueError("Empty passphrase")
# 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).
Args:
p8_private_key (byte string):
The private key wrapped into a PKCS#8 blob, DER encoded.
passphrase (byte string or 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
try:
p8_private_key = PBES1.decrypt(p8_private_key, passphrase)
found = True
except PbesError as e:
error_str = "PBES1[%s]" % str(e)
except ValueError:
error_str = "PBES1[Invalid]"
if not found:
try:
p8_private_key = PBES2.decrypt(p8_private_key, passphrase)
found = True
except PbesError as e:
error_str += ",PBES2[%s]" % str(e)
except ValueError:
error_str += ",PBES2[Invalid]"
if not found:
raise ValueError("Error decoding PKCS#8 (%s)" % error_str)
pk_info = DerSequence().decode(p8_private_key, nr_elements=(2, 3, 4))
if len(pk_info) == 2 and not passphrase:
raise ValueError("Not a valid clear PKCS#8 structure "
"(maybe it is encrypted?)")
#
# PrivateKeyInfo ::= SEQUENCE {
# version Version,
# privateKeyAlgorithm PrivateKeyAlgorithmIdentifier,
# privateKey PrivateKey,
# attributes [0] IMPLICIT Attributes OPTIONAL
# }
# Version ::= INTEGER
if pk_info[0] != 0:
raise ValueError("Not a valid PrivateKeyInfo SEQUENCE")
# PrivateKeyAlgorithmIdentifier ::= AlgorithmIdentifier
#
# EncryptedPrivateKeyInfo ::= SEQUENCE {
# encryptionAlgorithm EncryptionAlgorithmIdentifier,
# encryptedData EncryptedData
# }
# EncryptionAlgorithmIdentifier ::= AlgorithmIdentifier
# AlgorithmIdentifier ::= SEQUENCE {
# algorithm OBJECT IDENTIFIER,
# parameters ANY DEFINED BY algorithm OPTIONAL
# }
algo = DerSequence().decode(pk_info[1], nr_elements=(1, 2))
algo_oid = DerObjectId().decode(algo[0]).value
if len(algo) == 1:
algo_params = None
else:
try:
DerNull().decode(algo[1])
algo_params = None
except:
algo_params = algo[1]
# EncryptedData ::= OCTET STRING
private_key = DerOctetString().decode(pk_info[2]).payload
return (algo_oid, private_key, algo_params)

View File

@ -0,0 +1,435 @@
#
# PublicKey/_PBES.py : Password-Based Encryption functions
#
# ===================================================================
#
# Copyright (c) 2014, Legrandin <helderijs@gmail.com>
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
#
# 1. Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# 2. Redistributions in binary form must reproduce the above copyright
# notice, this list of conditions and the following disclaimer in
# the documentation and/or other materials provided with the
# distribution.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
# COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.
# ===================================================================
from Crypto import Random
from Crypto.Util.asn1 import (
DerSequence, DerOctetString,
DerObjectId, DerInteger,
)
from Crypto.Util.Padding import pad, unpad
from Crypto.Hash import MD5, SHA1, SHA224, SHA256, SHA384, SHA512
from Crypto.Cipher import DES, ARC2, DES3, AES
from Crypto.Protocol.KDF import PBKDF1, PBKDF2, scrypt
_OID_PBE_WITH_MD5_AND_DES_CBC = "1.2.840.113549.1.5.3"
_OID_PBE_WITH_MD5_AND_RC2_CBC = "1.2.840.113549.1.5.6"
_OID_PBE_WITH_SHA1_AND_DES_CBC = "1.2.840.113549.1.5.10"
_OID_PBE_WITH_SHA1_AND_RC2_CBC = "1.2.840.113549.1.5.11"
_OID_PBES2 = "1.2.840.113549.1.5.13"
_OID_PBKDF2 = "1.2.840.113549.1.5.12"
_OID_SCRYPT = "1.3.6.1.4.1.11591.4.11"
_OID_HMAC_SHA1 = "1.2.840.113549.2.7"
_OID_HMAC_SHA224 = "1.2.840.113549.2.8"
_OID_HMAC_SHA256 = "1.2.840.113549.2.9"
_OID_HMAC_SHA384 = "1.2.840.113549.2.10"
_OID_HMAC_SHA512 = "1.2.840.113549.2.11"
_OID_DES_EDE3_CBC = "1.2.840.113549.3.7"
_OID_AES128_CBC = "2.16.840.1.101.3.4.1.2"
_OID_AES192_CBC = "2.16.840.1.101.3.4.1.22"
_OID_AES256_CBC = "2.16.840.1.101.3.4.1.42"
class PbesError(ValueError):
pass
# 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
# }
#
# scrypt-params ::= SEQUENCE {
# salt OCTET STRING,
# costParameter INTEGER (1..MAX),
# blockSize INTEGER (1..MAX),
# parallelizationParameter INTEGER (1..MAX),
# keyLength INTEGER (1..MAX) OPTIONAL
# }
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
"""
@staticmethod
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.
"""
enc_private_key_info = DerSequence().decode(data)
encrypted_algorithm = DerSequence().decode(enc_private_key_info[0])
encrypted_data = DerOctetString().decode(enc_private_key_info[1]).payload
pbe_oid = DerObjectId().decode(encrypted_algorithm[0]).value
cipher_params = {}
if pbe_oid == _OID_PBE_WITH_MD5_AND_DES_CBC:
# PBE_MD5_DES_CBC
hashmod = MD5
ciphermod = DES
elif pbe_oid == _OID_PBE_WITH_MD5_AND_RC2_CBC:
# PBE_MD5_RC2_CBC
hashmod = MD5
ciphermod = ARC2
cipher_params['effective_keylen'] = 64
elif pbe_oid == _OID_PBE_WITH_SHA1_AND_DES_CBC:
# PBE_SHA1_DES_CBC
hashmod = SHA1
ciphermod = DES
elif pbe_oid == _OID_PBE_WITH_SHA1_AND_RC2_CBC:
# PBE_SHA1_RC2_CBC
hashmod = SHA1
ciphermod = ARC2
cipher_params['effective_keylen'] = 64
else:
raise PbesError("Unknown OID for PBES1")
pbe_params = DerSequence().decode(encrypted_algorithm[1], nr_elements=2)
salt = DerOctetString().decode(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)
class PBES2(object):
"""Encryption scheme with password-based key derivation
(defined in `PKCS#5 v2.0`__).
.. __: http://www.ietf.org/rfc/rfc2898.txt."""
@staticmethod
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 |
| | (called *N* or CPU/memory cost in scrypt). |
| | |
| | The default value for PBKDF2 is 1 000. |
| | The default value for scrypt is 16 384. |
+------------------+-----------------------------------------------+
| salt_size | Salt is used to thwart dictionary and rainbow |
| | attacks on passwords. The default value is 8 |
| | bytes. |
+------------------+-----------------------------------------------+
| block_size | *(scrypt only)* Memory-cost (r). The default |
| | value is 8. |
+------------------+-----------------------------------------------+
| parallelization | *(scrypt only)* CPU-cost (p). The default |
| | value is 1. |
+------------------+-----------------------------------------------+
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
cipher_mode = DES3.MODE_CBC
enc_oid = _OID_DES_EDE3_CBC
elif protection in ('PBKDF2WithHMAC-SHA1AndAES128-CBC',
'scryptAndAES128-CBC'):
key_size = 16
module = AES
cipher_mode = AES.MODE_CBC
enc_oid = _OID_AES128_CBC
elif protection in ('PBKDF2WithHMAC-SHA1AndAES192-CBC',
'scryptAndAES192-CBC'):
key_size = 24
module = AES
cipher_mode = AES.MODE_CBC
enc_oid = _OID_AES192_CBC
elif protection in ('PBKDF2WithHMAC-SHA1AndAES256-CBC',
'scryptAndAES256-CBC'):
key_size = 32
module = AES
cipher_mode = AES.MODE_CBC
enc_oid = _OID_AES256_CBC
else:
raise ValueError("Unknown PBES2 mode")
# Get random data
iv = randfunc(module.block_size)
salt = randfunc(prot_params.get("salt_size", 8))
# Derive key from password
if protection.startswith('PBKDF2'):
count = prot_params.get("iteration_count", 1000)
key = PBKDF2(passphrase, salt, key_size, count)
kdf_info = DerSequence([
DerObjectId(_OID_PBKDF2), # PBKDF2
DerSequence([
DerOctetString(salt),
DerInteger(count)
])
])
else:
# It must be scrypt
count = prot_params.get("iteration_count", 16384)
scrypt_r = prot_params.get('block_size', 8)
scrypt_p = prot_params.get('parallelization', 1)
key = scrypt(passphrase, salt, key_size,
count, scrypt_r, scrypt_p)
kdf_info = DerSequence([
DerObjectId(_OID_SCRYPT), # scrypt
DerSequence([
DerOctetString(salt),
DerInteger(count),
DerInteger(scrypt_r),
DerInteger(scrypt_p)
])
])
# Create cipher and use it
cipher = module.new(key, cipher_mode, iv)
encrypted_data = cipher.encrypt(pad(data, cipher.block_size))
enc_info = DerSequence([
DerObjectId(enc_oid),
DerOctetString(iv)
])
# Result
enc_private_key_info = DerSequence([
# encryptionAlgorithm
DerSequence([
DerObjectId(_OID_PBES2),
DerSequence([
kdf_info,
enc_info
]),
]),
DerOctetString(encrypted_data)
])
return enc_private_key_info.encode()
@staticmethod
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.
"""
enc_private_key_info = DerSequence().decode(data, nr_elements=2)
enc_algo = DerSequence().decode(enc_private_key_info[0])
encrypted_data = DerOctetString().decode(enc_private_key_info[1]).payload
pbe_oid = DerObjectId().decode(enc_algo[0]).value
if pbe_oid != _OID_PBES2:
raise PbesError("Not a PBES2 object")
pbes2_params = DerSequence().decode(enc_algo[1], nr_elements=2)
### Key Derivation Function selection
kdf_info = DerSequence().decode(pbes2_params[0], nr_elements=2)
kdf_oid = DerObjectId().decode(kdf_info[0]).value
kdf_key_length = None
# We only support PBKDF2 or scrypt
if kdf_oid == _OID_PBKDF2:
pbkdf2_params = DerSequence().decode(kdf_info[1], nr_elements=(2, 3, 4))
salt = DerOctetString().decode(pbkdf2_params[0]).payload
iteration_count = pbkdf2_params[1]
left = len(pbkdf2_params) - 2
idx = 2
if left > 0:
try:
kdf_key_length = pbkdf2_params[idx] - 0
left -= 1
idx += 1
except TypeError:
pass
# Default is HMAC-SHA1
pbkdf2_prf_oid = "1.2.840.113549.2.7"
if left > 0:
pbkdf2_prf_algo_id = DerSequence().decode(pbkdf2_params[idx])
pbkdf2_prf_oid = DerObjectId().decode(pbkdf2_prf_algo_id[0]).value
elif kdf_oid == _OID_SCRYPT:
scrypt_params = DerSequence().decode(kdf_info[1], nr_elements=(4, 5))
salt = DerOctetString().decode(scrypt_params[0]).payload
iteration_count, scrypt_r, scrypt_p = [scrypt_params[x]
for x in (1, 2, 3)]
if len(scrypt_params) > 4:
kdf_key_length = scrypt_params[4]
else:
kdf_key_length = None
else:
raise PbesError("Unsupported PBES2 KDF")
### Cipher selection
enc_info = DerSequence().decode(pbes2_params[1])
enc_oid = DerObjectId().decode(enc_info[0]).value
if enc_oid == _OID_DES_EDE3_CBC:
# DES_EDE3_CBC
ciphermod = DES3
key_size = 24
elif enc_oid == _OID_AES128_CBC:
# AES128_CBC
ciphermod = AES
key_size = 16
elif enc_oid == _OID_AES192_CBC:
# AES192_CBC
ciphermod = AES
key_size = 24
elif enc_oid == _OID_AES256_CBC:
# AES256_CBC
ciphermod = AES
key_size = 32
else:
raise PbesError("Unsupported PBES2 cipher")
if kdf_key_length and kdf_key_length != key_size:
raise PbesError("Mismatch between PBES2 KDF parameters"
" and selected cipher")
IV = DerOctetString().decode(enc_info[1]).payload
# Create cipher
if kdf_oid == _OID_PBKDF2:
if pbkdf2_prf_oid == _OID_HMAC_SHA1:
hmac_hash_module = SHA1
elif pbkdf2_prf_oid == _OID_HMAC_SHA224:
hmac_hash_module = SHA224
elif pbkdf2_prf_oid == _OID_HMAC_SHA256:
hmac_hash_module = SHA256
elif pbkdf2_prf_oid == _OID_HMAC_SHA384:
hmac_hash_module = SHA384
elif pbkdf2_prf_oid == _OID_HMAC_SHA512:
hmac_hash_module = SHA512
else:
raise PbesError("Unsupported HMAC %s" % pbkdf2_prf_oid)
key = PBKDF2(passphrase, salt, key_size, iteration_count,
hmac_hash_module=hmac_hash_module)
else:
key = scrypt(passphrase, salt, key_size, iteration_count,
scrypt_r, scrypt_p)
cipher = ciphermod.new(key, ciphermod.MODE_CBC, IV)
# Decrypt data
pt = cipher.decrypt(encrypted_data)
return unpad(pt, cipher.block_size)

View File

@ -0,0 +1,31 @@
# ===================================================================
#
# Copyright (c) 2014, Legrandin <helderijs@gmail.com>
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
#
# 1. Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# 2. Redistributions in binary form must reproduce the above copyright
# notice, this list of conditions and the following disclaimer in
# the documentation and/or other materials provided with the
# distribution.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
# COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.
# ===================================================================
__all__ = ['PEM', 'PKCS8']

View File

@ -0,0 +1,42 @@
# ===================================================================
#
# Copyright (c) 2014, Legrandin <helderijs@gmail.com>
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
#
# 1. Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# 2. Redistributions in binary form must reproduce the above copyright
# notice, this list of conditions and the following disclaimer in
# the documentation and/or other materials provided with the
# distribution.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
# COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.
# ===================================================================
__all__ = ["Integer"]
try:
from Crypto.Math._IntegerGMP import IntegerGMP as Integer
from Crypto.Math._IntegerGMP import implementation as _implementation
except (ImportError, OSError, AttributeError):
try:
from Crypto.Math._IntegerCustom import IntegerCustom as Integer
from Crypto.Math._IntegerCustom import implementation as _implementation
except (ImportError, OSError):
from Crypto.Math._IntegerNative import IntegerNative as Integer
_implementation = {}

View File

@ -0,0 +1,369 @@
# ===================================================================
#
# Copyright (c) 2014, Legrandin <helderijs@gmail.com>
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
#
# 1. Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# 2. Redistributions in binary form must reproduce the above copyright
# notice, this list of conditions and the following disclaimer in
# the documentation and/or other materials provided with the
# distribution.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
# COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.
# ===================================================================
"""Functions to create and test prime numbers.
:undocumented: __package__
"""
from Crypto import Random
from Crypto.Math.Numbers import Integer
from Crypto.Util.py3compat import iter_range
COMPOSITE = 0
PROBABLY_PRIME = 1
def miller_rabin_test(candidate, iterations, randfunc=None):
"""Perform a Miller-Rabin primality test on an integer.
The test is specified in Section C.3.1 of `FIPS PUB 186-4`__.
:Parameters:
candidate : integer
The number to test for primality.
iterations : integer
The maximum number of iterations to perform before
declaring a candidate a probable prime.
randfunc : callable
An RNG function where bases are taken from.
:Returns:
``Primality.COMPOSITE`` or ``Primality.PROBABLY_PRIME``.
.. __: http://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.186-4.pdf
"""
if not isinstance(candidate, Integer):
candidate = Integer(candidate)
if candidate in (1, 2, 3, 5):
return PROBABLY_PRIME
if candidate.is_even():
return COMPOSITE
one = Integer(1)
minus_one = Integer(candidate - 1)
if randfunc is None:
randfunc = Random.new().read
# Step 1 and 2
m = Integer(minus_one)
a = 0
while m.is_even():
m >>= 1
a += 1
# Skip step 3
# Step 4
for i in iter_range(iterations):
# Step 4.1-2
base = 1
while base in (one, minus_one):
base = Integer.random_range(min_inclusive=2,
max_inclusive=candidate - 2,
randfunc=randfunc)
assert(2 <= base <= candidate - 2)
# Step 4.3-4.4
z = pow(base, m, candidate)
if z in (one, minus_one):
continue
# Step 4.5
for j in iter_range(1, a):
z = pow(z, 2, candidate)
if z == minus_one:
break
if z == one:
return COMPOSITE
else:
return COMPOSITE
# Step 5
return PROBABLY_PRIME
def lucas_test(candidate):
"""Perform a Lucas primality test on an integer.
The test is specified in Section C.3.3 of `FIPS PUB 186-4`__.
:Parameters:
candidate : integer
The number to test for primality.
:Returns:
``Primality.COMPOSITE`` or ``Primality.PROBABLY_PRIME``.
.. __: http://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.186-4.pdf
"""
if not isinstance(candidate, Integer):
candidate = Integer(candidate)
# Step 1
if candidate in (1, 2, 3, 5):
return PROBABLY_PRIME
if candidate.is_even() or candidate.is_perfect_square():
return COMPOSITE
# Step 2
def alternate():
value = 5
while True:
yield value
if value > 0:
value += 2
else:
value -= 2
value = -value
for D in alternate():
if candidate in (D, -D):
continue
js = Integer.jacobi_symbol(D, candidate)
if js == 0:
return COMPOSITE
if js == -1:
break
# Found D. P=1 and Q=(1-D)/4 (note that Q is guaranteed to be an integer)
# Step 3
# This is \delta(n) = n - jacobi(D/n)
K = candidate + 1
# Step 4
r = K.size_in_bits() - 1
# Step 5
# U_1=1 and V_1=P
U_i = Integer(1)
V_i = Integer(1)
U_temp = Integer(0)
V_temp = Integer(0)
# Step 6
for i in iter_range(r - 1, -1, -1):
# Square
# U_temp = U_i * V_i % candidate
U_temp.set(U_i)
U_temp *= V_i
U_temp %= candidate
# V_temp = (((V_i ** 2 + (U_i ** 2 * D)) * K) >> 1) % candidate
V_temp.set(U_i)
V_temp *= U_i
V_temp *= D
V_temp.multiply_accumulate(V_i, V_i)
if V_temp.is_odd():
V_temp += candidate
V_temp >>= 1
V_temp %= candidate
# Multiply
if K.get_bit(i):
# U_i = (((U_temp + V_temp) * K) >> 1) % candidate
U_i.set(U_temp)
U_i += V_temp
if U_i.is_odd():
U_i += candidate
U_i >>= 1
U_i %= candidate
# V_i = (((V_temp + U_temp * D) * K) >> 1) % candidate
V_i.set(V_temp)
V_i.multiply_accumulate(U_temp, D)
if V_i.is_odd():
V_i += candidate
V_i >>= 1
V_i %= candidate
else:
U_i.set(U_temp)
V_i.set(V_temp)
# Step 7
if U_i == 0:
return PROBABLY_PRIME
return COMPOSITE
from Crypto.Util.number import sieve_base as _sieve_base_large
## The optimal number of small primes to use for the sieve
## is probably dependent on the platform and the candidate size
_sieve_base = set(_sieve_base_large[:100])
def test_probable_prime(candidate, randfunc=None):
"""Test if a number is prime.
A number is qualified as prime if it passes a certain
number of Miller-Rabin tests (dependent on the size
of the number, but such that probability of a false
positive is less than 10^-30) and a single Lucas test.
For instance, a 1024-bit candidate will need to pass
4 Miller-Rabin tests.
:Parameters:
candidate : integer
The number to test for primality.
randfunc : callable
The routine to draw random bytes from to select Miller-Rabin bases.
:Returns:
``PROBABLE_PRIME`` if the number if prime with very high probability.
``COMPOSITE`` if the number is a composite.
For efficiency reasons, ``COMPOSITE`` is also returned for small primes.
"""
if randfunc is None:
randfunc = Random.new().read
if not isinstance(candidate, Integer):
candidate = Integer(candidate)
# First, check trial division by the smallest primes
if int(candidate) in _sieve_base:
return PROBABLY_PRIME
try:
map(candidate.fail_if_divisible_by, _sieve_base)
except ValueError:
return COMPOSITE
# These are the number of Miller-Rabin iterations s.t. p(k, t) < 1E-30,
# with p(k, t) being the probability that a randomly chosen k-bit number
# is composite but still survives t MR iterations.
mr_ranges = ((220, 30), (280, 20), (390, 15), (512, 10),
(620, 7), (740, 6), (890, 5), (1200, 4),
(1700, 3), (3700, 2))
bit_size = candidate.size_in_bits()
try:
mr_iterations = list(filter(lambda x: bit_size < x[0],
mr_ranges))[0][1]
except IndexError:
mr_iterations = 1
if miller_rabin_test(candidate, mr_iterations,
randfunc=randfunc) == COMPOSITE:
return COMPOSITE
if lucas_test(candidate) == COMPOSITE:
return COMPOSITE
return PROBABLY_PRIME
def generate_probable_prime(**kwargs):
"""Generate a random probable prime.
The prime will not have any specific properties
(e.g. it will not be a *strong* prime).
Random numbers are evaluated for primality until one
passes all tests, consisting of a certain number of
Miller-Rabin tests with random bases followed by
a single Lucas test.
The number of Miller-Rabin iterations is chosen such that
the probability that the output number is a non-prime is
less than 1E-30 (roughly 2^{-100}).
This approach is compliant to `FIPS PUB 186-4`__.
:Keywords:
exact_bits : integer
The desired size in bits of the probable prime.
It must be at least 160.
randfunc : callable
An RNG function where candidate primes are taken from.
prime_filter : callable
A function that takes an Integer as parameter and returns
True if the number can be passed to further primality tests,
False if it should be immediately discarded.
:Return:
A probable prime in the range 2^exact_bits > p > 2^(exact_bits-1).
.. __: http://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.186-4.pdf
"""
exact_bits = kwargs.pop("exact_bits", None)
randfunc = kwargs.pop("randfunc", None)
prime_filter = kwargs.pop("prime_filter", lambda x: True)
if kwargs:
raise ValueError("Unknown parameters: " + kwargs.keys())
if exact_bits is None:
raise ValueError("Missing exact_bits parameter")
if exact_bits < 160:
raise ValueError("Prime number is not big enough.")
if randfunc is None:
randfunc = Random.new().read
result = COMPOSITE
while result == COMPOSITE:
candidate = Integer.random(exact_bits=exact_bits,
randfunc=randfunc) | 1
if not prime_filter(candidate):
continue
result = test_probable_prime(candidate, randfunc)
return candidate
def generate_probable_safe_prime(**kwargs):
"""Generate a random, probable safe prime.
Note this operation is much slower than generating a simple prime.
:Keywords:
exact_bits : integer
The desired size in bits of the probable safe prime.
randfunc : callable
An RNG function where candidate primes are taken from.
:Return:
A probable safe prime in the range
2^exact_bits > p > 2^(exact_bits-1).
"""
exact_bits = kwargs.pop("exact_bits", None)
randfunc = kwargs.pop("randfunc", None)
if kwargs:
raise ValueError("Unknown parameters: " + kwargs.keys())
if randfunc is None:
randfunc = Random.new().read
result = COMPOSITE
while result == COMPOSITE:
q = generate_probable_prime(exact_bits=exact_bits - 1, randfunc=randfunc)
candidate = q * 2 + 1
if candidate.size_in_bits() != exact_bits:
continue
result = test_probable_prime(candidate, randfunc=randfunc)
return candidate

View File

@ -0,0 +1,392 @@
# ===================================================================
#
# Copyright (c) 2018, Helder Eijs <helderijs@gmail.com>
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
#
# 1. Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# 2. Redistributions in binary form must reproduce the above copyright
# notice, this list of conditions and the following disclaimer in
# the documentation and/or other materials provided with the
# distribution.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
# COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.
# ===================================================================
import abc
from Crypto.Util.py3compat import iter_range, bord, bchr, ABC
from Crypto import Random
class IntegerBase(ABC):
# Conversions
@abc.abstractmethod
def __int__(self):
pass
@abc.abstractmethod
def __str__(self):
pass
@abc.abstractmethod
def __repr__(self):
pass
@abc.abstractmethod
def to_bytes(self, block_size=0):
pass
@staticmethod
@abc.abstractmethod
def from_bytes(byte_string):
pass
# Relations
@abc.abstractmethod
def __eq__(self, term):
pass
@abc.abstractmethod
def __ne__(self, term):
pass
@abc.abstractmethod
def __lt__(self, term):
pass
@abc.abstractmethod
def __le__(self, term):
pass
@abc.abstractmethod
def __gt__(self, term):
pass
@abc.abstractmethod
def __ge__(self, term):
pass
@abc.abstractmethod
def __nonzero__(self):
pass
__bool__ = __nonzero__
@abc.abstractmethod
def is_negative(self):
pass
# Arithmetic operations
@abc.abstractmethod
def __add__(self, term):
pass
@abc.abstractmethod
def __sub__(self, term):
pass
@abc.abstractmethod
def __mul__(self, factor):
pass
@abc.abstractmethod
def __floordiv__(self, divisor):
pass
@abc.abstractmethod
def __mod__(self, divisor):
pass
@abc.abstractmethod
def inplace_pow(self, exponent, modulus=None):
pass
@abc.abstractmethod
def __pow__(self, exponent, modulus=None):
pass
@abc.abstractmethod
def __abs__(self):
pass
@abc.abstractmethod
def sqrt(self, modulus=None):
pass
@abc.abstractmethod
def __iadd__(self, term):
pass
@abc.abstractmethod
def __isub__(self, term):
pass
@abc.abstractmethod
def __imul__(self, term):
pass
@abc.abstractmethod
def __imod__(self, term):
pass
# Boolean/bit operations
@abc.abstractmethod
def __and__(self, term):
pass
@abc.abstractmethod
def __or__(self, term):
pass
@abc.abstractmethod
def __rshift__(self, pos):
pass
@abc.abstractmethod
def __irshift__(self, pos):
pass
@abc.abstractmethod
def __lshift__(self, pos):
pass
@abc.abstractmethod
def __ilshift__(self, pos):
pass
@abc.abstractmethod
def get_bit(self, n):
pass
# Extra
@abc.abstractmethod
def is_odd(self):
pass
@abc.abstractmethod
def is_even(self):
pass
@abc.abstractmethod
def size_in_bits(self):
pass
@abc.abstractmethod
def size_in_bytes(self):
pass
@abc.abstractmethod
def is_perfect_square(self):
pass
@abc.abstractmethod
def fail_if_divisible_by(self, small_prime):
pass
@abc.abstractmethod
def multiply_accumulate(self, a, b):
pass
@abc.abstractmethod
def set(self, source):
pass
@abc.abstractmethod
def inplace_inverse(self, modulus):
pass
@abc.abstractmethod
def inverse(self, modulus):
pass
@abc.abstractmethod
def gcd(self, term):
pass
@abc.abstractmethod
def lcm(self, term):
pass
@staticmethod
@abc.abstractmethod
def jacobi_symbol(a, n):
pass
@staticmethod
def _tonelli_shanks(n, p):
"""Tonelli-shanks algorithm for computing the square root
of n modulo a prime p.
n must be in the range [0..p-1].
p must be at least even.
The return value r is the square root of modulo p. If non-zero,
another solution will also exist (p-r).
Note we cannot assume that p is really a prime: if it's not,
we can either raise an exception or return the correct value.
"""
# See https://rosettacode.org/wiki/Tonelli-Shanks_algorithm
if n in (0, 1):
return n
if p % 4 == 3:
root = pow(n, (p + 1) // 4, p)
if pow(root, 2, p) != n:
raise ValueError("Cannot compute square root")
return root
s = 1
q = (p - 1) // 2
while not (q & 1):
s += 1
q >>= 1
z = n.__class__(2)
while True:
euler = pow(z, (p - 1) // 2, p)
if euler == 1:
z += 1
continue
if euler == p - 1:
break
# Most probably p is not a prime
raise ValueError("Cannot compute square root")
m = s
c = pow(z, q, p)
t = pow(n, q, p)
r = pow(n, (q + 1) // 2, p)
while t != 1:
for i in iter_range(0, m):
if pow(t, 2**i, p) == 1:
break
if i == m:
raise ValueError("Cannot compute square root of %d mod %d" % (n, p))
b = pow(c, 2**(m - i - 1), p)
m = i
c = b**2 % p
t = (t * b**2) % p
r = (r * b) % p
if pow(r, 2, p) != n:
raise ValueError("Cannot compute square root")
return r
@classmethod
def random(cls, **kwargs):
"""Generate a random natural integer of a certain size.
:Keywords:
exact_bits : positive integer
The length in bits of the resulting random Integer number.
The number is guaranteed to fulfil the relation:
2^bits > result >= 2^(bits - 1)
max_bits : positive integer
The maximum length in bits of the resulting random Integer number.
The number is guaranteed to fulfil the relation:
2^bits > result >=0
randfunc : callable
A function that returns a random byte string. The length of the
byte string is passed as parameter. Optional.
If not provided (or ``None``), randomness is read from the system RNG.
:Return: a Integer object
"""
exact_bits = kwargs.pop("exact_bits", None)
max_bits = kwargs.pop("max_bits", None)
randfunc = kwargs.pop("randfunc", None)
if randfunc is None:
randfunc = Random.new().read
if exact_bits is None and max_bits is None:
raise ValueError("Either 'exact_bits' or 'max_bits' must be specified")
if exact_bits is not None and max_bits is not None:
raise ValueError("'exact_bits' and 'max_bits' are mutually exclusive")
bits = exact_bits or max_bits
bytes_needed = ((bits - 1) // 8) + 1
significant_bits_msb = 8 - (bytes_needed * 8 - bits)
msb = bord(randfunc(1)[0])
if exact_bits is not None:
msb |= 1 << (significant_bits_msb - 1)
msb &= (1 << significant_bits_msb) - 1
return cls.from_bytes(bchr(msb) + randfunc(bytes_needed - 1))
@classmethod
def random_range(cls, **kwargs):
"""Generate a random integer within a given internal.
:Keywords:
min_inclusive : integer
The lower end of the interval (inclusive).
max_inclusive : integer
The higher end of the interval (inclusive).
max_exclusive : integer
The higher end of the interval (exclusive).
randfunc : callable
A function that returns a random byte string. The length of the
byte string is passed as parameter. Optional.
If not provided (or ``None``), randomness is read from the system RNG.
:Returns:
An Integer randomly taken in the given interval.
"""
min_inclusive = kwargs.pop("min_inclusive", None)
max_inclusive = kwargs.pop("max_inclusive", None)
max_exclusive = kwargs.pop("max_exclusive", None)
randfunc = kwargs.pop("randfunc", None)
if kwargs:
raise ValueError("Unknown keywords: " + str(kwargs.keys))
if None not in (max_inclusive, max_exclusive):
raise ValueError("max_inclusive and max_exclusive cannot be both"
" specified")
if max_exclusive is not None:
max_inclusive = max_exclusive - 1
if None in (min_inclusive, max_inclusive):
raise ValueError("Missing keyword to identify the interval")
if randfunc is None:
randfunc = Random.new().read
norm_maximum = max_inclusive - min_inclusive
bits_needed = cls(norm_maximum).size_in_bits()
norm_candidate = -1
while not 0 <= norm_candidate <= norm_maximum:
norm_candidate = cls.random(
max_bits=bits_needed,
randfunc=randfunc
)
return norm_candidate + min_inclusive

View File

@ -0,0 +1,111 @@
# ===================================================================
#
# Copyright (c) 2018, Helder Eijs <helderijs@gmail.com>
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
#
# 1. Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# 2. Redistributions in binary form must reproduce the above copyright
# notice, this list of conditions and the following disclaimer in
# the documentation and/or other materials provided with the
# distribution.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
# COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.
# ===================================================================
from ._IntegerNative import IntegerNative
from Crypto.Util.number import long_to_bytes, bytes_to_long
from Crypto.Util._raw_api import (load_pycryptodome_raw_lib,
create_string_buffer,
get_raw_buffer, backend,
c_size_t, c_ulonglong)
from Crypto.Random.random import getrandbits
c_defs = """
int monty_pow(const uint8_t *base,
const uint8_t *exp,
const uint8_t *modulus,
uint8_t *out,
size_t len,
uint64_t seed);
"""
_raw_montgomery = load_pycryptodome_raw_lib("Crypto.Math._modexp", c_defs)
implementation = {"library": "custom", "api": backend}
class IntegerCustom(IntegerNative):
@staticmethod
def from_bytes(byte_string):
return IntegerCustom(bytes_to_long(byte_string))
def inplace_pow(self, exponent, modulus=None):
exp_value = int(exponent)
if exp_value < 0:
raise ValueError("Exponent must not be negative")
# No modular reduction
if modulus is None:
self._value = pow(self._value, exp_value)
return self
# With modular reduction
mod_value = int(modulus)
if mod_value < 0:
raise ValueError("Modulus must be positive")
if mod_value == 0:
raise ZeroDivisionError("Modulus cannot be zero")
# C extension only works with odd moduli
if (mod_value & 1) == 0:
self._value = pow(self._value, exp_value, mod_value)
return self
# C extension only works with bases smaller than modulus
if self._value >= mod_value:
self._value %= mod_value
max_len = len(long_to_bytes(max(self._value, exp_value, mod_value)))
base_b = long_to_bytes(self._value, max_len)
exp_b = long_to_bytes(exp_value, max_len)
modulus_b = long_to_bytes(mod_value, max_len)
out = create_string_buffer(max_len)
error = _raw_montgomery.monty_pow(
out,
base_b,
exp_b,
modulus_b,
c_size_t(max_len),
c_ulonglong(getrandbits(64))
)
if error:
raise ValueError("monty_pow failed with error: %d" % error)
result = bytes_to_long(get_raw_buffer(out))
self._value = result
return self

Some files were not shown because too many files have changed in this diff Show More