Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Python 3.5 error 'Only byte strings can be passed to C code' #35

Closed
dineshbvadhia opened this issue Oct 8, 2016 · 16 comments
Closed

Python 3.5 error 'Only byte strings can be passed to C code' #35

dineshbvadhia opened this issue Oct 8, 2016 · 16 comments
Labels

Comments

@dineshbvadhia
Copy link

On python 3.5, I replaced pycrypto with pycryptodome and got the error 'Only byte strings can be passed to C code' when running my program which previously worked with pycrypto.

Is there a solution?

@Legrandin
Copy link
Owner

The error is generated on a certain line of your program, with a call to a method in pycryptodome.

Could you share the type of all the parameters you pass there, plus the name of the method?

@dineshbvadhia
Copy link
Author

Unfortunately, I uninstalled pycryptodome.

I use the Python Anaconda distribution. In this case, which is the preferred install method: drop-in replacement or pycryptodomex? I don't use a virtualenv.

First, I uninstalled pycrypto and then installed pycryptodome but got the error.

Next, I tried installing pycryptodomex by first uninstalling pycrypto but got the same error.

Has pycryptodome passed your tests under python 3.5x?

@Varbin
Copy link
Contributor

Varbin commented Feb 10, 2017

Would you mind sharing the code you use?

@Boco10
Copy link

Boco10 commented Mar 16, 2017

from Crypto.Cipher import AES

key = bytes.fromhex("0123456789abcdef0123456789abcdef")
IV = 16 * '\x00' # Initialization vector: discussed later
mode = AES.MODE_CBC
encryptor = AES.new(str(key), mode, IV=IV)

text = 'j' * 64 + 'i' * 128
ciphertext = encryptor.encrypt(text)

File "C:/GIT/indoor-testfw-wired/network/delete.py", line 6, in
encryptor = AES.new(str(key), mode, IV=IV)
File "C:\Python3.6\lib\site-packages\Crypto\Cipher\AES.py", line 264, in new
return create_cipher(sys.modules[name], key, mode, *args, **kwargs)
File "C:\Python3.6\lib\site-packages\Crypto\Cipher_init
.py", line 130, in _create_cipher
return modes[mode](factory, **kwargs)
File "C:\Python3.6\lib\site-packages\Crypto\Cipher_mode_cbc.py", line 232, in _create_cbc_cipher
cipher_state = factory._create_base_cipher(kwargs)
File "C:\Python3.6\lib\site-packages\Crypto\Cipher\AES.py", line 131, in _create_base_cipher
expect_byte_string(key)
File "C:\Python3.6\lib\site-packages\Crypto\Util_raw_api.py", line 175, in expect_byte_string
raise TypeError("Only byte strings can be passed to C code")
TypeError: Only byte strings can be passed to C code

Any idea how to fix it? Like no example found on the internet how to overcome this issue... Thanks

@Varbin
Copy link
Contributor

Varbin commented Mar 18, 2017

instead of str(key) use key. If you have strings encode them before using them:

string = "Hello world"
encoded = string.encode("latin-1")

This applies for all data (keys, ivs, data) that are passed to pycrypto(dome) while using Python 3, but not with Python 2.

You can also predefine bytestrings:
bs = b"bytestring"

One final note on latin-1 encoding: This is for 1:1 encoding from string to bytes. If unicode content (like üä, etc.) are used the must be preserved use utf-8. Please pad after encoding with utf-8 because the encoded size may be bigger than the original string as one character sometimes does not equal to one byte.

@ghost
Copy link

ghost commented Aug 21, 2017

This also happens on my machine with Python 3.6 if I try to run basic examples like:
http://docs.python-guide.org/en/latest/scenarios/crypto/#id5

@Legrandin
Copy link
Owner

As mentioned earlier, in that example all strings should be prefixed with 'b'.
For instance:

b'This is a key123'

instead of:

'This is a key123'

@gallonyin
Copy link

use a prefix of 'b' or 'B' 

or

bytes(s, encoding = "utf8")  # str to bytes

Example:

# coding: utf-8
from Crypto.Cipher import AES
import base64
pad_it = lambda s: bytes(s+(16 - len(s)%16)*PADDING, encoding='utf8')
key = b'1234567812345678'
iv = b'1234567812345678'
source = 'Test String'
generator = AES.new(key, AES.MODE_CBC, iv)
crypt = generator.encrypt(pad_it(source))
cryptedStr = base64.b64encode(crypt)

@shiva1333
Copy link

#!/usr/bin/env python
import base64
import numpy as np
import time
from Crypto import Random
from Crypto.Cipher import AES
from time import sleep
from picamera import PiCamera
import cv2
import sys

BS = 16
pad = lambda s: bytes(s + (BS - len(s) % BS) * chr(BS - len(s) % BS),encoding='utf8')
#pad = lambda s: s + (32 - len(s) % 32) * ' '
unpad = lambda s : s[0:-ord(s[-1])]
class AESCipher:
def init( self, key ):
self.key = key

def encrypt( self, raw ):
    raw = pad(raw)
    iv = Random.new().read( AES.block_size )
    cipher = AES.new( self.key, AES.MODE_CBC, iv )
    return base64.b64encode( iv + cipher.encrypt( raw ) )

def decrypt( self, enc ):
    enc = base64.b64decode(enc)
    iv = enc[:16]
    cipher = AES.new(self.key, AES.MODE_CBC, iv )
    return unpad(cipher.decrypt( enc[16:] ))

ts = time.clock()
camera=PiCamera()
camera.resolution = (320,240)
camera.framerate = 24
time.sleep(2)
image = np.empty((320,240, 3), dtype=np.uint8)
camera.capture(image, 'bgr')

ser = np.array_str(image);
cipher = AESCipher('mysecretpassword')
encrypted = cipher.encrypt(pad(ser))
decrypted = cipher.decrypt(encrypted)

print(time.clock()-ts)
print('{}'.format(ser==decrypted))
print(len(ser))
print(len(encrypted)
print(len(decrypted))

Please help! the code is showing the following error : TypeError: can't concat bytes to str

@Varbin
Copy link
Contributor

Varbin commented Dec 15, 2017

@shivanshuIITR

This errors says that somewhere in your code you "mix" normal text (str) with text encoded to bytes which is NOT possible.

If you want help, would you mind
a) formatting your code (put it between two of ```)
b) say on which line your error occurs, or better a full traceback (I can't run your code - I currently have no access to a raspberry Pi with PiCamera). Your error might also have nothing to do with PyCryptodome - as I can't run your code I have not idea.
c) tell if you run this code on Python 2 or 3? Your shebang line (#!/usr/bin/env python) suggests Py 2, but your error suggests Py 3 . If you use Python 3 all data passed to a cipher object (including the key) must be bytes, including the key (in your example it is a normal string).

@ShyavanS
Copy link

I have a similar error when trying to use the code below with pycryptodome instead of pycrypto:

from Crypto.Cipher import AES
import base64
import sys, os

class colors(object):
PURPLE = '\033[95m'
CYAN = '\033[96m'
DARKCYAN = '\033[36m'
BLUE = '\033[94m'
GREEN = '\033[92m'
YELLOW = '\033[93m'
RED = '\033[91m'
BOLD = '\033[1m'
UNDERLINE = '\033[4m'
ENDC = '\033[0m'

class Forensics(object):

decrypter = AES.new("one".rjust(32), AES.MODE_ECB)
decoded = None

def __init__(self, encryption_key):

	if(len(encryption_key) > 32):
		print("Keys must be at most 32 characters in length. Yours was {0}".format(len(encryption_key)))
		print("Program exiting now.")
		sys.exit()
	else:
		self.key = encryption_key.rjust(32)
		self.encrypter = AES.new(self.key, AES.MODE_ECB)
		print(colors.PURPLE + "Key passed to encrypter cipher: {0}".format(self.key.strip()) + colors.ENDC)

def encrypt_message(self, to_encrypt):

	if(len(to_encrypt) > 128):
		print("Your message has to be at most 128 characters in length! Yours was {0} characters. Exiting now.".format(len(to_encrypt)))
		sys.exit()
	else:
		message = to_encrypt.rjust(128)

		cipher = AES.new(self.key, AES.MODE_ECB)

		self.encoded = base64.b64encode(cipher.encrypt(message))

		self.decoded = base64.b64decode(self.encoded)

def decrypt_attempt(self, key_attempt):
	decrypter = AES.new(key_attempt.rjust(32), AES.MODE_ECB)
	decrypted_attempt = decrypter.decrypt(self.decoded)

	verify = key_attempt.rjust(32)

	if(verify == self.key):
		print(colors.GREEN + "Decryption key is correct." + colors.ENDC)
		print(colors.BOLD + colors.CYAN + "Decrypted message: {0}".format(decrypted_attempt.strip()) + colors.ENDC)
		return True
	else:
		print(colors.RED + "Decryption key is incorrect." + colors.ENDC)
		print("That key resulted in the following: {0}".format(base64.b64encode(decrypted_attempt)))
		return False

def run_game(self):
	# This method moves some logic into this function, so students don't have to write it.
	choice = raw_input("Ready to play? Hit enter to continue or Q to quit.").lower()

	# If the user says Q, then just quit the program.
	if choice == 'q':
		sys.exit()
	else:
		# Otherwise, let's begin the game by clearing the screen.
		os.system('clear') # command is 'cls' on Windows

		# Tell the user what the encrypted version of the message is.
		print("The encrypted message is {0}".format(self.encoded))

		# Set attempt_status to false -- until this is made true, the program keeps 
		# asking you to guess the key.
		attempt_status = False

		while attempt_status == False:
			# Ask the user what the encryption key COULD be.
			attempt = raw_input("What might the encryption key be? ")

			# Ask the vault if that's the right encryption key.
			attempt_status = self.decrypt_attempt(attempt)
			# If the vault accepts the key, then attempt_status gets set to True, and you win the game.
			# If the key is incorrect, attempt_status stays False and you get asked all over again.

			# This just prints a new line. That's all.
			print("\n")

When I run this it gives the following error:

Traceback (most recent call last):
File "L:\Jr. DEEP\main.py", line 2, in
from forensics import Forensics
File "L:\Jr. DEEP\forensics.py", line 17, in
class Forensics(object):
File "L:\Jr. DEEP\forensics.py", line 19, in Forensics
decrypter = AES.new("one".rjust(32), AES.MODE_ECB)
File "C:\Users\Oasis\AppData\Roaming\Python\Python36\site-packages\Crypto\Cipher\AES.py", line 202, in new
return create_cipher(sys.modules[name], key, mode, *args, **kwargs)
File "C:\Users\Oasis\AppData\Roaming\Python\Python36\site-packages\Crypto\Cipher_init
.py", line 55, in _create_cipher
return modes[mode](factory, **kwargs)
File "C:\Users\Oasis\AppData\Roaming\Python\Python36\site-packages\Crypto\Cipher_mode_ecb.py", line 175, in _create_ecb_cipher
cipher_state = factory._create_base_cipher(kwargs)
File "C:\Users\Oasis\AppData\Roaming\Python\Python36\site-packages\Crypto\Cipher\AES.py", line 89, in _create_base_cipher
expect_byte_string(key)
File "C:\Users\Oasis\AppData\Roaming\Python\Python36\site-packages\Crypto\Util_raw_api.py", line 194, in expect_byte_string
raise TypeError("Only byte strings can be passed to C code")
TypeError: Only byte strings can be passed to C code

Could someone please tell me where I am going wrong?

@Varbin
Copy link
Contributor

Varbin commented Jan 14, 2018

@ShyavanS You pass a string to the cipher instead of bytes. So instead of

AES.new("one".rjust(32), AES.MODE_ECB)

just use

AES.new("one".rjust(32).encode('latin-1'), AES.MODE_ECB).

This is standard Python 3 behaviour, PyCrypto was just a bit more "forgiving" and did this encoding "under the hood".

@ShyavanS
Copy link

Thank you so much!

It started to work but now I am getting this error:

Traceback (most recent call last):
File "J:\main.py", line 20, in
vault.encrypt_message(message)
File "J:\forensics.py", line 43, in encrypt_message
self.encoded = base64.b64encode(cipher.encrypt(message))
File "C:\Users\Oasis\AppData\Roaming\Python\Python36\site-packages\Crypto\Cipher_mode_ecb.py", line 117, in encrypt
expect_byte_string(plaintext)
File "C:\Users\Oasis\AppData\Roaming\Python\Python36\site-packages\Crypto\Util_raw_api.py", line 194, in expect_byte_string
raise TypeError("Only byte strings can be passed to C code")
TypeError: Only byte strings can be passed to C code

Do you mind helping me out with this as well?

@Varbin
Copy link
Contributor

Varbin commented Jan 14, 2018

I think it should be cipher.encrypt(message.encode()) instead of cipher.encrypt(message). This applies for all encryption operations.
Please not that e.g. Umlauts (öäüß) may not work.

You also need to encode key_attempt as well before using.

As I said above:

If you use Python 3 all data passed to a cipher object (including the key) must be bytes. This applies for plain- and ciphertexts, too. If you need to "revert" the encoding, use .decode().

@ShyavanS
Copy link

Thank you very much! The program works completely now!

screenbeard added a commit to screenbeard/optus-sagemcom-fast-3864-hacks that referenced this issue Apr 15, 2018
pycrypto at https://github.com/sfbahr/PyCrypto-Wheels hasn't been updated in three years and won't install on windows
pycryptodome is a "drop in replacement" for pycrypto, but throws errors on this script.
changing the KEY and IV strings to b'<string>' was listed as a solution here: Legrandin/pycryptodome#35
@answerquest
Copy link

This stackoverflow question gave a simple solution : append .encode('utf-8') to what you pass to cypher.encrypt().

https://stackoverflow.com/questions/46052752/using-aes-encrypt-get-raise-typeerroronly-byte-strings-can-be-passed-to-c-code/50725058#50725058

Works on my end on python3, using Cryptodome instead of Crypto in import statements, from pycryptodomex module . This is following my experience with a project: pycryptodomex works on heroku servers as well as in standalone binaries whereas the default pycrypto and its replacement pycryptodome don't.

mattimustang pushed a commit to mattimustang/optus-sagemcom-fast-3864-hacks that referenced this issue Oct 4, 2018
pycrypto at https://github.com/sfbahr/PyCrypto-Wheels hasn't been updated in three years and won't install on windows
pycryptodome is a "drop in replacement" for pycrypto, but throws errors on this script.
changing the KEY and IV strings to b'<string>' was listed as a solution here: Legrandin/pycryptodome#35
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

8 participants