pysodium crypto_box_open throwing ValueError - python

I'm using libsodium with the python bindings in pysodium 0.6.6
When using crypto_box and crypto_box_open, I always get a ValueError. Here is a simple example:
import pysodium
import random
serverPK, serverSK = pysodium.crypto_box_keypair()
clientPK, clientSK = pysodium.crypto_box_keypair()
testText = "test message"
nonce1 = str(random.random())
nonce2 = str(random.random())
cipherText = pysodium.crypto_secretbox(testText,nonce1,clientPK)
message = pysodium.crypto_secretbox_open(cipherText, nonce2, clientSK)
print message
And here is the error:
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "/Users/.../pysodium-0.6.6/pysodium/__init__.py", line 181, in crypto_box_open
__check(sodium.crypto_box_open(msg, padded, ctypes.c_ulonglong(len(padded)), nonce, pk, sk))
File "/Users/.../pysodium-0.6.6/pysodium/__init__.py", line 70, in __check
raise ValueError
ValueError

crypto_box_keypair() creates a key pair, to be used with crypto_box().
crypto_secretbox() is not asymmetric encryption: a single key is used both to encrypt and to decrypt. If this is actually what you want, the key can be generated that way:
key = pysodium.randombytes(pysodium.crypto_secretbox_KEYBYTES)
For a given key, the nonce has to be unique for each message, and has to be pysodium.crypto_secretbox_NONCEBYTES bytes long. It can be a simple counter, or a random nonce:
nonce = pysodium.randombytes(pysodium.crypto_secretbox_NONCEBYTES)
Unlike the secret key, a nonce can be public. But it has to be the same for encrypting and for decrypting:
cipherText = pysodium.crypto_secretbox(testText, nonce, key)
message = pysodium.crypto_secretbox_open(cipherText, nonce, key)
The libsodium documentation provides examples on how to use box and secretbox, which can be easily translated to pysodium equivalents.

Related

Python fastecdsa/ecdsa signing error, cannot convert integer to bits

I am trying to implement ScroogeCoin using the fastecdsa library. I currently run into an error that happens when my create_coins function is called. The error points to the signing function (tx["signature"]) and says that it cannot convert an integer type to a byte.
import hashlib
import json
from fastecdsa import keys, curve, ecdsa
class ScroogeCoin(object):
def __init__(self):
self.private_key, self.public_key = keys.gen_keypair(curve.secp256k1)
self.address = hashlib.sha256(json.dumps(self.public_key.x).encode()).hexdigest()
self.chain = []
self.current_transactions = []
def create_coins(self, receivers: dict):
"""
Scrooge adds value to some coins
:param receivers: {account:amount, account:amount, ...}
"""
tx = {
"sender" : self.address, # address,
# coins that are created do not come from anywhere
"location": {"block": -1, "tx": -1},
"receivers" : receivers,
}
tx["hash"] = hashlib.sha256(json.dumps(tx).encode()).hexdigest()# hash of tx
tx["signature"] = ecdsa.sign(self.private_key, tx["hash"])# signed hash of tx
self.current_transactions.append(tx)
...
When this function is ran in the main function:
...
Scrooge = ScroogeCoin()
users = [User(Scrooge) for i in range(10)]
Scrooge.create_coins({users[0].address:10, users[1].address:20, users[3].address:50})
...
It produces this error:
Traceback (most recent call last):
File "D:\Scrooge_coin_assignmnet.py", line 216, in <module>
main()
File "D:\Scrooge_coin_assignmnet.py", line 197, in main
Scrooge.create_coins({users[0].address:10, users[1].address:20, users[3].address:50})
File "D:\Scrooge_coin_assignmnet.py", line 27, in create_coins
tx["signature"] = ecdsa.sign(self.private_key, tx["hash"])# signed hash of tx
File "C:\Users\d\AppData\Local\Programs\Python\Python311\Lib\site-packages\fastecdsa\ecdsa.py", line 36, in sign
rfc6979 = RFC6979(msg, d, curve.q, hashfunc, prehashed=prehashed)
File "C:\Users\d\AppData\Local\Programs\Python\Python311\Lib\site-packages\fastecdsa\util.py", line 25, in __init__
self.msg = msg_bytes(msg)
File "C:\Users\d\AppData\Local\Programs\Python\Python311\Lib\site-packages\fastecdsa\util.py", line 153, in msg_bytes
raise ValueError('Msg "{}" of type {} cannot be converted to bytes'.format(
ValueError: Msg "21783419755125685845542189331366569080312572314742637241373298325693730090205" of type <class 'int'> cannot be converted to bytes
I've tried to play around and change it to a byte by using encode on the tx["hash"] as well things like bytes.fromhex() but it still gives the same error. I wanted to ask others who are more skilled and see if they can see how I am messing up.

What would be an acceptable bit size for RSA encryption of an image? (PYTHON)

I'm coding a encryption module using pycryptodomex and I have run into an issue regarding the plaintext being to long for image encryption (ValueError). For my image encryption I convert the image into bytes and then encrypt the bytes. Currently I am using a key-pair with a bit size of 2048. I have tried using a key size of 4096 which takes a significantly longer amount of time to generate and use (≈100s). My question is is there a more efficient algorithms for encrypting images apart from RSA or AES (I use both however I am only getting this error whilst testing the asymmetric image encryption) and how would I encrypt images using these algorithms. I have also though of alternatives to increasing the key size these are using a if statement to prevent the user from trying to encrypt an image that is larger than the limit of encrypting an image using a 2048-bit RSA key, in this case what is the image size limit for encrypting using this kind of key? Another alternative I have though of is using compression to make the image size smaller however I would like to avoid this as this may mean loosing image quality, if nothing else works would encrypting compressed images work in the same was as encrypting standard images.
Here is the traceback for the error:
$ python -m unittest Test_TIENC_REVISED.py
EEEE....
======================================================================
ERROR: test_decrypt_failure (Test_TIENC_REVISED.TestImageEncA)
----------------------------------------------------------------------
Traceback (most recent call last):
File "C:\Users\probm\Desktop\tienc\tests\Test_TIENC_REVISED.py", line 58, in test_decrypt_failure
ciphertext = self.image_enc.encrypt(self.image_path)
File "C:\Users\probm\Desktop\tienc\tests\tienc_revised.py", line 71, in encrypt
ciphertext = cipher.encrypt(img_bytes)
File "C:\Users\probm\Desktop\tienc\venv\lib\site-packages\Cryptodome\Cipher\PKCS1_OAEP.py", line 115, in encrypt
raise ValueError("Plaintext is too long.")
ValueError: Plaintext is too long.
======================================================================
ERROR: test_encrypt_decrypt (Test_TIENC_REVISED.TestImageEncA)
----------------------------------------------------------------------
Traceback (most recent call last):
File "C:\Users\probm\Desktop\tienc\tests\Test_TIENC_REVISED.py", line 50, in test_encrypt_decrypt
ciphertext = self.image_enc.encrypt(self.image_path)
File "C:\Users\probm\Desktop\tienc\tests\tienc_revised.py", line 71, in encrypt
ciphertext = cipher.encrypt(img_bytes)
File "C:\Users\probm\Desktop\tienc\venv\lib\site-packages\Cryptodome\Cipher\PKCS1_OAEP.py", line 115, in encrypt
raise ValueError("Plaintext is too long.")
ValueError: Plaintext is too long.
Here is the code I use to test the encryption (commented out code is what I previously had):
class TestImageEncA(unittest.TestCase):
def setUp(self):
self.image_path = imgpath
self.private_key, self.public_key = tienc_revised.generate_key_a_4096() #self.private_key, self.public_key = tienc_revised.generate_key_a()
self.image_enc = tienc_revised.ImageEncA(self.public_key)
def test_encrypt_decrypt(self):
ciphertext = self.image_enc.encrypt(self.image_path)
output_path = "decrypted_image.jpg"
self.image_enc.decrypt(ciphertext, self.private_key, output_path)
with open(self.image_path, "rb") as f1, open(output_path, "rb") as f2:
self.assertEqual(f1.read(), f2.read())
def test_decrypt_failure(self):
wrong_key = RSA.generate(4096).export_key() #wrong_key = RSA.generate(2048).export_key()
ciphertext = self.image_enc.encrypt(self.image_path)
output_path = "decrypted_image.jpg"
self.assertRaises(Exception, self.image_enc.decrypt, ciphertext, wrong_key, output_path)
Here is the main code for encryption:
class ImageEncA:
""" asymmetric image encryption """
def __init__(self, public_key):
self.public_key = RSA.import_key(public_key)
def encrypt(self, image_path):
with open(image_path, "rb") as f:
img_bytes = f.read()
cipher = PKCS1_OAEP.new(self.public_key)
ciphertext = cipher.encrypt(img_bytes)
return ciphertext
def decrypt(self, ciphertext, private_key, output_path):
private_key = RSA.import_key(private_key)
cipher = PKCS1_OAEP.new(private_key)
img_bytes = cipher.decrypt(ciphertext)
with open(output_path, "wb") as f:
f.write(img_bytes)
Here is the code for generating keys:
# Asymetric key generation
def generate_key_a():
key = RSA.generate(2048)
private_key = key.export_key()
public_key = key.publickey().export_key()
return private_key, public_key
# Symetric key generation
def generate_key_s():
key = os.urandom(32)
return key
# Asymetric key generation with a larger bit size
def generate_key_a_4096():
key = RSA.generate(4096)
private_key_b = key.export_key()
public_key_b = key.publickey().export_key()
return private_key_b, public_key_b
So how would I fix this problem?

Encrypt/Decrypt message using PGPy

I am trying out PGPy and I get an error whenever I encrypt or decrypt a message.
These are the codes that I use. Documentation and examples can be found here
Code to encrypt a message using the public key:
import pgpy
key_pub = '''BEGIN PUBLIC KEY BLOCK...END PUBLIC KEY BLOCK'''.lstrip()
message = "It worked!"
# import ASCII formatted public key
pub_key = pgpy.PGPKey()
pub_key.parse(key_pub)
# create new message
text_message = pgpy.PGPMessage.new(message)
# encrypt a message using pub key
encrypted_message = pub_key.encrypt(text_message)
print(encrypted_message)
This one gave the output but with a message
UserWarning: Selected compression algorithm not in key preferences
encrypted_message = pub_key.encrypt(text_message)
which I don't get why.
Code to decrypt the message using the priv key:
import pgpy
key_priv ='''*BEGIN PRIV KEY BLOCK...END PRIV KEY BLOCK'''.lstrip()
cipher_text = '''BEGIN PGP MESSAGE...END PGP MESSAGE'''.lstrip()
# import ASCII formatted private key
priv_key = pgpy.PGPKey()
priv_key.parse(key_priv)
message_from_blob = pgpy.PGPMessage.from_blob(cipher_text)
# decrypts a message using priv key
decrypted_message = priv_key.decrypt(cipher_text)
print(decrypted_message)
And this one does not work at all.This is the error I get.
Traceback (most recent call last):
File "C:/Users..practice.py", line 13, in <module>
decrypted_message = priv_key.decrypt(cipher_text)
File "C:\Users...venv\lib\site-packages\pgpy\decorators.py", line 126, in _action
self.check_attributes(key)
File "C:\Users...venv\lib\site-packages\pgpy\decorators.py", line 111, in check_attributes
raise PGPError("Expected: {attr:s} == {eval:s}. Got: {got:s}"
pgpy.errors.PGPError: Expected: is_unlocked == True. Got: False
Assuming that you have an RSA public and private key, encrypting a string or file can be done simply:
PUBLIC_KEY_FILE = 'path/to/keyfile/my_pub_key.asc'
pub_key, _ = pgpy.PGPKey.from_file(str(PUBLIC_KEY_FILE))
# Encrypt string
txt_msg = pgpy.PGPMessage.new("Hello PGPy World")
print('txt_msg.is_encrypted')
print(txt_msg.is_encrypted)
print('txt_msg.message')
print(txt_msg.message)
encrypted_txt_msg = pub_key.encrypt(txt_msg)
print('encrypted_txt_msg.is_encrypted')
print(encrypted_txt_msg.is_encrypted)
print('encrypted_txt_msg.message')
print(encrypted_txt_msg.message)
# Encrypt file
f_t_e = pgpy.PGPMessage.new(str(FILE_TO_ENCRYPT),file=True)
print('f_t_e.is_encrypted')
print(f_t_e.is_encrypted)
encrypted_f_t_e = pub_key.encrypt(f_t_e)
print('encrypted_f_t_e.is_encoded')
print(encrypted_f_t_e.is_encrypted)
Assuming you have the RSA private key associated with the above public key (in the recipient list) and a text file containing the passphrase, decrypting a string or file is quite straightforward:
# Load passphrase from file
with open(PASSPHRASE_FILE,'r') as ppfp:
PASSPHRASE = ppfp.readline().replace('\n','')
print(PASSPHRASE)
# Load private key
PRIVATE_KEY_FILE='path/to/keyfile/my_prv_key.gpg'
prv_key, _ = pgpy.PGPKey.from_file(str(PRIVATE_KEY_FILE))
# Unlock private key
print(prv_key.is_protected) # Should be True
with prv_key.unlock(PASSPHRASE):
print(prv_key.is_unlocked) #Should be True
# Decrypt string
decrypted_txt_msg = prv_key.decrypt(encrypted_txt_msg)
print('decrypted_txt_msg.is_encrypted')
print(decrypted_txt_msg.is_encrypted)
print('decrypted_txt_msg.message')
print(decrypted_txt_msg.message)
# Decrypt file
decrypted_f_t_e = prv_key.decrypt(encrypted_f_t_e)
print('decrypted_f_t_e.is_encrypted')
print(decrypted_f_t_e.is_encrypted)

ecdsa signing key format

I am trying to create a public/private key pair using python.
I have created a private key using the following method:
private_key = ''.join(['%x' % random.randrange(16) for x in range(0, 64)])
using this private key I have attempted to use a ecdsa graph to generate to corresponding public key
def privateKeyToPublicKey(s):
sk = ecdsa.SigningKey.from_string(s, curve=ecdsa.SECP256k1)
vk = sk.verifying_key
return ('\04' + sk.verifying_key.to_string())
I have not been able to create the signing key (sk) due to a formatting error where my string is in the wrong format. But I am not sure how/what format the string s should be for SigningKey to work.
I get the following error when running the script:
Traceback (most recent call last):
File "address.py", line 23, in <module>
privateKeyToPublicKey(private_key)
File "address.py", line 20, in privateKeyToPublicKey
sk = ecdsa.SigningKey.from_string(s, curve=ecdsa.SECP256k1)
File "/usr/local/lib/python3.6/dist-packages/ecdsa/keys.py", line
149, in from_string
assert len(string) == curve.baselen, (len(string), curve.baselen)
AssertionError: (64, 32)
Here's a more complete code sample for clarity about the answer. Python3.
from ecdsa import SigningKey, SECP256k1
import sha3, random, binascii
private_key = ''.join(['%x' % random.randrange(16) for x in range(0, 64)])
private_key = bytes(private_key, 'utf-8')
private_key = binascii.unhexlify(private_key)
priv = SigningKey.from_string(private_key, curve=SECP256k1)
pub = priv.get_verifying_key().to_string()
keccak = sha3.keccak_256()
keccak.update(pub)
address = keccak.hexdigest()[24:]
print(address, priv.to_string().hex())
I have realised my error, the input must be in bytes, the private key in hex format. In python2 you may use:
private_key.decode('hex')
or you may use
binascii.unhexlify
in python3

PyCrypto export/import of signature

created a client-server application with sockets and I am trying to transfer the signature from client to server. I convert it from tuple to string and then back to tuple. But signing stops working. How to resolve this?
from Crypto.Hash import SHA256
from Crypto.PublicKey import RSA
public_key_file = open('public.pem','r')
public_key = RSA.importKey(public_key_file.read())
signature = "(90392831408741910958006452852395405116864328891950288888434929210668328849466319419951775157374761930395371626801844365799774616689823184955256615103504859356914334395152128600862146719619859327119380994333493461955529620578485576675021993313219918726432622856542420570716350341841652548574072964446809201965L,)"
signature_tuple = signature.split(",")
message = "Block_Height:1 From:c52030257a864a67ae4ef8a726282ed2b6b273fbccb474885027a857 To:2 Amount:3"
if public_key.verify(message, signature_tuple) == True:
print "Signature valid"
.
Traceback (most recent call last):
File "C:\Users\kucerjan\Desktop\test\sco\public_test.py", line 12, in <module>
if public_key.verify(message, signature_tuple) == True:
File "build\bdist.win32\egg\Crypto\PublicKey\RSA.py", line 221, in verify
return pubkey.pubkey.verify(self, M, signature)
File "build\bdist.win32\egg\Crypto\PublicKey\pubkey.py", line 126, in verify
return self._verify(M, signature)
File "build\bdist.win32\egg\Crypto\PublicKey\RSA.py", line 257, in _verify
return self.key._verify(m, s)
File "build\bdist.win32\egg\Crypto\PublicKey\_slowmath.py", line 73, in _verify
return self._encrypt(sig) == m
File "build\bdist.win32\egg\Crypto\PublicKey\_slowmath.py", line 65, in _encrypt
return pow(m, self.e, self.n)
TypeError: unsupported operand type(s) for pow(): 'str', 'long', 'long'
This signature is already converted to string using str(signature). I basically need to convert it to string and back.
Function reference: https://www.dlitz.net/software/pycrypto/api/current/Crypto.PublicKey.RSA._RSAobj-class.html#verify
Public key:
-----BEGIN PUBLIC KEY-----
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDFiMH7Lbd4JPFug8TaxX1DT8ad
lzzGm7CG1js0IQn2pCPPWBS+io1i0iUPmj78IOtUuoBqtEYGPgwqguYHozBuvdJy
Lcz4C2bYcjb2l8mQ4PM7iaCN4eHB+4xa+iJduogTjq8gx5m3j5mttEGUbZc2Q/AO
yde592P2iuRIrXcLuwIDAQAB
-----END PUBLIC KEY-----
The problem is in deserializing the signature tuple.
PyCrypto is expecting a tuple with an integer as the first value, you are passing it a string with a beginning paren "(" and then a string version of a number.
Instead of doing this:
signature_tuple = signature.split(",")
do this
signature_tuple = eval(signature)
That will properly parse the signature.
Now, there are security risks with using eval. So, if I were you, I'd come up with a better serialization/deserialization process.
The best way is to use PKCS1_v1_5 for real applications in combination with base64 for encoding and decoding the signature between client and server. No eval is needed.
from Crypto.Signature import PKCS1_v1_5
from Crypto.Hash import SHA
from Crypto.PublicKey import RSA
import base64
message = 'To be signed'
key = RSA.importKey(open('privkey.der').read())
h = SHA.new(message)
signer = PKCS1_v1_5.new(key)
signature = signer.sign(h)
signature_enc = str(base64.b64encode(signature))
#print signature_enc
signature_dec = str(base64.b64decode (signature_enc))
#print sugnature_dec
key = RSA.importKey(open('pubkey.der').read())
h = SHA.new(message)
verifier = PKCS1_v1_5.new(key)
if verifier.verify(h, signature_dec):
print "The signature is authentic."
else:
print "The signature is not authentic."

Categories

Resources