4

我有一个测试工具,它使用 Twofish 作为加密算法在将数据发送到服务器之前对其进行加密。该代码是用 C++ 编写的,并使用 Bruce Schneier 的优化 C 实现 ( https://www.schneier.com/code/twofish-optimized-c.zip )。我需要将此工具移植到 Python,并且我正在使用 twofish 模块(https://pypi.python.org/pypi/twofish/0.3.0)。我可以加密和解密 16 个字符长度的字符串,但对于其他字符串长度,它会给出错误“ValueError:无效块长度”。

如何使用 Python 的 Twofish 模块加密和解密大数据?

>>> from twofish import Twofish
>>> key = binascii.unhexlify('8CACBE276491F6FF4B1EC0E9CFD52E76')
>>> t = Twofish(key)
>>> cipher_text = T.encrypt('deadbeaf12345678')
>>> plain_text = t.decrypt(cipher_text)
>>> plain_text
'deadbeaf12345678'
>>> cipher_text = t.encrypt('deadbeaf12345678hello world 1234')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/usr/local/lib/python2.7/site-packages/twofish.py", line 69, in encrypt
    raise ValueError('invalid block length')
ValueError: invalid block length

更新: 我正在尝试解决此问题的另一种方法。我从 Bruce Schneier 的优化 C 实现(https://www.schneier.com/code/twofish-optimized-c.zip)创建了一个 Windows DLL,twofish.dll。此外,我还使用 __declspec(dllexport) 导出了用于编码和解码成员函数的包装函数。

我正在使用 ctype.CDLL 函数在 Python 中加载这个 DLL。编码函数的原型是:

__declspec(dllexport) int encode(unsigned char *key, unsigned char *in, unsigned int inlen, unsigned char *out, unsigned int outbuflen, unsigned int& outlen)

我应该如何在 Python 脚本中定义参数类型?

import ctypes
import binascii
import requests

twofish_dll = ctypes.CDLL("twofish.dll")

encode = twofish_dll.encode

f = open('test.xml', 'r')
plain_text = f.read()
f.close()

cipher_text = ctypes.create_string_buffer(8192)
cipher_text_lenght = (ctypes.c_uint)()

KCS = '8CACBE276491F6FF4B1EC0E9CFD52E76'
key = binascii.unhexlify(KCS)

encode.argtypes = [ctypes.c_char_p, ctypes.c_char_p, ctypes.c_uint, ctypes.c_char_p, ctypes.c_uint, ctypes.POINTER(ctypes.c_uint)]
encode(ctypes.c_char_p(key), ctypes.c_char_p(plain_text), len(plain_text), cipher_text, 8192, ctypes.byref(cipher_text_lenght))

执行上面的代码时会抛出以下错误:

Traceback (most recent call last):
  File "C:\Data\sepm_test.py", line 21, in <module>
    encode(ctypes.c_char_p(key), ctypes.c_char_p(plain_text), len(plain_text), cipher_text, 8192, ctypes.byref(cipher_text_lenght))
TypeError: bytes or integer address expected instead of str instance
4

3 回答 3

2

Twofish 是一种分组密码,一次只加密 16 个八位字节。引用文档

创建一个 twofish.Twofish 实例,其密钥长度为 ]0, 32],然后对 16 字节块使用加密和解密方法。

所有值都必须是二进制字符串(Python 2 上为 str,Python 3 上为 bytes)

[警告]这应该在合理的密码模式下使用,如 CTR 或 CBC。如果您不知道这意味着什么,您可能应该使用更高级别的库。

请注意警告,CBC 或 CTR 不是火箭科学,但如果你天真地使用 Twofish,它的安全性就会受到严重损害。

于 2015-03-25T01:48:31.230 回答
2

我终于通过将 Bruce Schneier 为 Twofish 优化的 C 实现(https://www.schneier.com/code/twofish-optimized-c.zip)编译到一个 DLL 中并使用 ctypes 模块加载该 DLL 来解决了这个问题。

import ctypes
import binascii
import requests

twofish_dll = ctypes.CDLL("twofish.dll")

encode = twofish_dll.encode

f = open('test.xml', 'r')
plain_text = f.read()
f.close()
plain_text_buffer = ctypes.create_string_buffer(str.encode(plain_text))
plain_text_buffer_length = (ctypes.c_uint)(len(plain_text_buffer))

cipher_text_buffer = ctypes.create_string_buffer(8192)
cipher_text_buffer_length = (ctypes.c_uint)(len(cipher_text_buffer))
cipher_text_length = ctypes.c_uint(0)

KCS = '8CACBE276491F6FF4B1EC0E9CFD52E76'
key = binascii.unhexlify(KCS)

encode.argtypes = [ctypes.c_char_p, ctypes.c_char_p, ctypes.c_uint, ctypes.c_char_p, ctypes.c_uint, ctypes.POINTER(ctypes.c_uint)]
encode.restype = ctypes.c_int

encode(ctypes.c_char_p(key), plain_text_buffer, plain_text_buffer_length, cipher_text_buffer, cipher_text_buffer_length, ctypes.pointer(cipher_text_length))
于 2015-03-27T07:38:32.630 回答
0
'''
The encryption algo is "Twofish Encryption"
'''

import sys
from twofish import Twofish
import os

def usages():
    print('-------Usgaes of TwoFish encrypt/decrypt script------------')
    print('')
    print('    --help or -h to get help of the script')
    print('    --encrypt or -e to encrypt a file')
    print('    --decrypt or -d to decrypt a file')
    print('    --filename or -f filename to be encrypted or decrypted')
    print('    --keypath or -k filename to pass keyfile name')
    print('')
    print('python TwofishEncryptDecrypt.py --encrypt --filename <FileName> --keypath <KeyFilename>')
    print('python TwofishEncryptDecrypt.py --decrypt --filename <FileName> --keypath <KeyFilename>')
    print('')
    print('-----------------------------End---------------------------')

def argumentParse(argsList):
    if '--help' in argsList or '-h' in argsList:
        usages()
        sys.exit(0)
    elif ('-e' in argsList and '-d' in argsList) or ('--encrypt' in argsList and '--decrypt' in argsList):
        print('Both action not allowed together')
        usages()
        sys.exit(0)

    i = 0
    for item in argsList:
        if item == '-e' or item == '--encrypt':
            arguments.update({'action': 'encryption'})
        elif item == '-d' or item == '--decrypt':
            arguments.update({'action': 'decryption'})
        elif item == '-f' or item == '--filename':
            if os.path.exists(argsList[i+1]) and os.path.getsize(argsList[i+1])>0:
                arguments.update({'filename': argsList[i+1]})
            else:
                print("[No such file or Directory] or [File Might Be empty] :",argsList[i+1])
                pass
        elif item == '-k' or item == '--keypath':
            arguments.update({'keypath': argsList[i+1]})
        else:
            pass
        i+=1

    return arguments

class encryptDecrypt():

    BLOCK_SIZE = 16
    twofish_passphrase = ''

    def encryptTwofish(self, filename):
        fileContent = open(filename,'r').read()

        paddingBytesLength = self.BLOCK_SIZE - (len(fileContent) % self.BLOCK_SIZE)

        paddingBytes = ''
        for i in range(paddingBytesLength):
            #paddingBytes += '00'
            paddingBytes += ' '

        #fileContent = fileContent + bytearray.fromhex(paddingBytes).decode("utf-8")
        fileContent = fileContent.decode('utf-8') + paddingBytes

        iteration_count = int(len(fileContent) / self.BLOCK_SIZE)
        encryptedFileContent = ''.encode()
        for i in range(iteration_count):
            encryptedFileContent += self.T.encrypt(fileContent[self.BLOCK_SIZE * i : (i+1) * self.BLOCK_SIZE].encode())

        return encryptedFileContent

    def decryptTwofish(self, filename):
        decryptedFileContent = ''
        encryptedFileContent = open(filename,'rb').read()

        iteration_count = int(len(encryptedFileContent) / self.BLOCK_SIZE)

        for i in range(iteration_count):
            decryptedFileContent += self.T.decrypt(encryptedFileContent[self.BLOCK_SIZE * i : (i+1) * self.BLOCK_SIZE]).decode()

        return decryptedFileContent.strip()

    def readKey(self, path):
        keyValue = open(path,'r')
        for line in keyValue:
            self.twofish_passphrase=line.rstrip('\n')
            self.T = Twofish(self.twofish_passphrase)

# entry point
    def writeFile(self, filename, fileContent):
        resultFile = open(filename,'wb')
        resultFile.write(fileContent)
        resultFile.close()

if __name__ == "__main__":
    arguments = {}
    argsList = sys.argv
    arguments = argumentParse(argsList)

    if not 'keypath' in arguments or not 'action' in arguments or not 'filename' in arguments:
        usages()
        sys.exit()

    twofish_encrypt = encryptDecrypt()
    twofish_encrypt.readKey(arguments['keypath'])
    if (arguments['action'] == 'encryption'):
        twofish_encrypt.writeFile(arguments['filename'], twofish_encrypt.encryptTwofish(arguments['filename']))
    elif (arguments['action'] == 'decryption'):
        twofish_encrypt.writeFile(arguments['filename'], twofish_encrypt.decryptTwofish(arguments['filename']))

# Remove ending NULL character from decrypted string
于 2019-11-13T19:56:04.913 回答