Non-ASCII character '\x97' in file, but no encoding declared - python

I'm trying to encode the contents of a Python script in Linux. I just started off with a simple script called test.py -
# !/app/logs/Python/bin/python3
# -*- coding: ascii -*-
print ("hi")
Once I have the script, I execute the vim -x test.py and enter the encryption key twice. Then save the file as normal and then execute the script using python test.py
I tried almost all the examples provided in the link here but still i end up getting the below error -
SyntaxError: Non-ASCII character '\x97' in file test.py on line 1, but no encoding declared; see http://www.python.org/peps/pep-0263.html for details
I checked the default encoding using print sys.getdefaultencoding() and it is acsii.
What am i missing here. kindly clarify. I know this question is a duplicate but none of the solutions helped.

Python knows how to execute clear text Python source code. If you encrypt the source file, it no longer contains valid Python source and cannot be directly executed.
There are 2 possible ways here. First is to only obfuscate your source. You should be aware that obfuscation is not security, and a user could recover some Python source with some work (not necessarily the original source, because comments and doc strings could have been stripped off and variable names could have been changed). You could read How do I protect Python code? and google for python obfuscate to find some possible ways of obfuscating Python source and their trade-offs.
The good news with obfuscated source is that it can be used by anybody without any password.
Second way is to encrypt the source. If you use a decent tool, you can assume that it will be impossible to read or execute the file without knowing the key. In that sense, vim crypto has not the highest possible reputation. In the simplest way (for example with your example vim -x) you will have to decrypt the file to execute it. Unfortunately, good crypto modules are not shipped in a standard Python installation and must be downloaded from pypi. Well known crypto modules include pycrypto and cryptography.
You can then encrypt the most part of the code, and then at run time ask for the key, decrypt it and execute it. Still a serious job but feasible.
Alternatively, you could build in another language (C/C++) a decryptor that decrypts the remaining of the file and feed it into a python interpretor, but functionally, this is only a variant of the above method.
As per your comment I assume that you want to encrypt the source code and decrypt it (with the password) at run time. The princips are:
build a Python script that will take another arbitrary Python script, encode it with a secure crypto module and prepend it with some decrypting code.
at run time, the prepended code will ask for the password, decrypt the encrypted code exec it
The builder could be (this code uses the cryptography module):
import cryptography.fernet
import cryptography.hazmat.primitives.kdf.pbkdf2
import cryptography.hazmat.primitives.hashes
import cryptography.hazmat.backends
import base64
import os
import sys
# the encryption function
def encrypt_source(infile, outfile, passwd):
with open(infile, 'rb') as fdin: # read original file
plain_data = fdin.read()
salt, key = gen_key(passwd) # derive a key from the password
f = cryptography.fernet.Fernet(key)
crypted_data = f.encrypt(plain_data) # encrypt the original code
with open(outfile, "w") as fdout: # prepend a decoding block
fdout.write("""#! /usr/bin/env python
import cryptography.fernet
import cryptography.hazmat.primitives.kdf.pbkdf2
import cryptography.hazmat.primitives.hashes
import cryptography.hazmat.backends
import base64
import os
def gen_key(passwd, salt): # key derivation
kdf = cryptography.hazmat.primitives.kdf.pbkdf2.PBKDF2HMAC(
algorithm = cryptography.hazmat.primitives.hashes.SHA256(),
length = 32,
salt = salt,
iterations = 100000,
backend = cryptography.hazmat.backends.default_backend()
)
return base64.urlsafe_b64encode(kdf.derive(passwd))
passwd = input("Password:") # ask for the password
salt = base64.decodebytes({})
key = gen_key(passwd.encode(), salt) # derive the key from the password and the original salt
crypted_source = base64.decodebytes( # decode (base64) the crypted source
b'''{}'''
)
f = cryptography.fernet.Fernet(key)
plain_source = f.decrypt(crypted_source) # decrypt it
exec(plain_source) # and exec it
""".format(base64.encodebytes(salt),
base64.encodebytes(crypted_data).decode()))
# derive a key from a password and a random salt
def gen_key(passwd, salt=None):
if salt is None: salt = os.urandom(16)
kdf = cryptography.hazmat.primitives.kdf.pbkdf2.PBKDF2HMAC(
algorithm = cryptography.hazmat.primitives.hashes.SHA256(),
length = 32,
salt = salt,
iterations = 100000,
backend = cryptography.hazmat.backends.default_backend()
)
return salt, base64.urlsafe_b64encode(kdf.derive(passwd))
if __name__ == '__main__':
if len(sys.argv) != 3:
print("Usage {} infile outfile".format(sys.argv[0]))
sys.exit(1)
passwd = input("Password:").encode() # ask for a password
encrypt_source(sys.argv[1], sys.argv[2], passwd) # and generates an encrypted Python script

Related

How can I SHA1-HMAC a string in Python

I've been trying to figure out how to use HMAC to hash a string in Python for a TOTP generator I've already made in PHP. In PHP, I use the command echo -n "0x00000000035362f9" | xxd -r -p | openssl dgst -sha1 -mac HMAC -macopt hexkey:0080700040024c8581c2 which returns the desired value 7289e7b135d54b86a462a53da93ef6ad28b902f8. However, when I use the hmac library in Python 3.10
from hashlib import sha1
from hmac import new
key = "00000000035362f9"
msg = "0080700040024c8581c2"
byte_key = bytes(key, "UTF-8")
message = msg.encode()
print(new(byte_key, message, sha1).hexdigest())
print(new(message, byte_key, sha1).hexdigest())
The printed values are b05fe172b6a8a20767c18e1bfba159f0ea54c2bd and 0fe109b32b17aeff840255558e6b5c8ff3d8a115, neither match what I want.
I've tried making the key and message hex values first, I've made them raw bytes using b'0080700040024c8581c2', encoding them using UTF-8 and ASCII, and none of the solutions have worked.
I've looked at other post relating to this, and none of them worked.
Python hmac (sha1) calculation
Python HMAC OpenSSL equivalent
hmac returning different hexdigest values to openssl
Why Python and Node.js's HMAC result is different in this code?
Implementing SHA1-HMAC with Python
#jasonharper's answer from the comments was correct.
from hashlib import sha1
from hmac import new
from binascii import unhexlify
msg = unhexlify("00000000035362f9")
key = unhexlify("0080700040024c8581c2")
print(new(key, msg, sha1).hexdigest())

Convert Bytes[] to Python Bytes Using PythonNet

I have a PythonNet C# bytes[] object and I need to convert it to Python's bytes.
Is there a way to do this using Python?
Here is what I'm doing to get the bytes. I need to go into the winstore's credential store and export out a client certificate.
import clr, os
import requests
from cryptography.hazmat.primitives.serialization.pkcs12 import (
load_key_and_certificates,
)
from cryptography.hazmat.primitives.serialization import Encoding, NoEncryption
from cryptography.hazmat.backends import default_backend
clr.AddReference("System")
clr.AddReference("System.Security.Cryptography.X509Certificates")
clr.AddReference("System.Security.Cryptography")
from System.Security.Cryptography.X509Certificates import (
X509Store,
StoreName,
StoreLocation,
OpenFlags,
X509CertificateCollection,
X509FindType,
X509Certificate2,
X509ContentType,
)
store = X509Store(StoreName.My, StoreLocation.CurrentUser)
store.Open(OpenFlags.ReadOnly)
serial_number = (
'SUPER SECRET SERIAL NUMBER'
)
collection = store.Certificates.Find(
X509FindType.FindBySerialNumber, serial_number, False
)
cert = collection.get_Item(0)
pkcs12 = cert.Export(X509ContentType.Pkcs12, "<Secret Password>")
# I have System.Bytes[] need python bytes to use with pyopenssl/cryptography
This code is using the pythonnet library to access the crypto API on windows. It then dumps a client certificate, found by a serial number, to bytes. When the clr library returns the value, it's a system.Bytes[]. This is incompatible with other libraries, so I need a way to convert this object to Python bytes.
You should be able to use the Python function bytes (as #JabberJabber mentioned). The following works for me (c_sharp_bytes actually being the result of a C# function) with Python 3.8:
from System import Byte, Array
python_bytes = b'Some bytes'
# Python -> C#
c_sharp_bytes = Array[Byte](python_bytes) # <System.Byte[] object at 0x000001A33CC2A3D0>
# C# -> Python
python_bytes = bytes(c_sharp_bytes) # b'Some bytes'

Why using python-gnupg makes .xlsx file unreadable?

I have some problems with python-gnupg package.
I need to decrypt an encrypted .xlsx file, do something with it and then encrypt it back again. So I've written a small script to see how it would work and.. it doesn't.
Unfortuanately, searching SO doesn't return many answers (somehow python-gpg related questions are not very well received here), nor does googling. The ones I've found which seemed relevant but aren't: SO: privilege related, python-gnupg group: add str to crypt object.
The problem can be put as follows:
The program encrypts and decrypts the .xlsx file, but it is unreadable after the process.
If I encrypt the file using command line, it can't be decrypted from python. Instead I see the following error:
File "/gnupg-2.0.2-py3.5.egg/gnupg/_parsers.py", line 1419 in _handle_status raise ValueError("Unknown status message: %r" % key)
ValueError: Unknown status message: 'FAILURE'
Code works fine with text files, but if I'd encrypt such a file using CLI, decryption from within python results in the same error as above.
Here is the code:
# -*- coding: utf-8 -*-
import gnupg
gpg = gnupg.GPG(homedir='~/.gnupg/')
with open('echo.xlsx', 'rb') as f:
status = gpg.encrypt(
f, '5955128B',
output='echo.xlsx.gpg')
print ('ok: ', status.ok)
print ('status: ', status.status)
print ('stderr: ', status.stderr)
a = input('press any key to continue')
with open('echo.xslx.gpg', 'rb',) as f:
status = gpg.decrypt_file(
f, passphrase='passphrase', output='echo2.xslx')
print ('ok: ', status.ok)
print ('status: ', status.status)
print ('stderr: ', status.stderr)
I'm on python 3.5, python-gnupg 2.0.2, GnuPG 2.0.29.
I would really appreciate some help on this, I desperately need this to work.

Storing the secrets (passwords) in a separate file

What's the simplest way to store the application secrets (passwords, access tokens) for a Python script? I thought it'd be a *.yml file like in Ruby but surprisingly I found that it wasn't the case. So what is it then? What are the most simplest solutions?
I want to put them in a separate file because that way I'll be able not to push that file to a github repository.
I think storing credentials inside another *py file is your safest bet. Then just import it. Example would look like this
config.py
username = "xy"
password = "abcd"
main.py
import config
login(config.username, config.password)
I was dealing exactly the same question and actually ended up with the same solution as kecer suggested. Since I need to use it in dozens of scripts, I've created own library. Let me share this solution with you.
credlib.py -- universal library to handle credentials
class credential:
def __init__(self, hostname, username, password):
self.hostname = hostname
self.username = username
self.password = password
mycredentials.py -- my local file to store all credentials
from credlib import credential
sys_prod = credential("srv01", "user", "pass")
sys_stg = credential("srv02", "user", "pass")
sys_db = credential("db01", "userdb", "passdb")
mysystemlib.py -- this is a general library to access my system (both new credential system and legacy is supported)
from credlib import credential
def system_login(*args): # this is new function definition
#def system_login(hostname, username, password): # this was previous function definition
if len(args) == 1 and isinstance(args[0], credential):
hostname = args[0].hostname
username = args[0].username
password = args[0].password
elif len(args) == 3:
hostname = args[0]
username = args[1]
password = args[2]
else:
raise ValueError('Invalid arguments')
do_login(hostname, username, password) # this is original system login call
main.py -- main script that combines credentials and system libs
from mycredentials import sys_stg, sys_db
import mysystemlib
...
mysystemlib.system_login(sys_stg)
Please note that the legacy hostname/username/password way still works so it does not affect old scripts:
mysystemlib.system_login("srv02", "user", "pass")
This has a lot benefits:
same credential system across all our python scripts
files with passwords are separated (files can have more strict permissions)
files are not stored in our git repositories (excluded via .gitignore) so that our python scripts/libs can be shared with others without exposing credentials (everyone defines their own credentials in their local files)
if a password needs to be changed, we do it at a single place only
Personally I prefer to use yaml files, with the pyyaml library.
Documentation here: https://pyyaml.org/wiki/PyYAMLDocumentation
Creating a .gitignore rule is very quick and painless and there is zero chances of making a mistake. You can added the rule with echo on Linux / UNIX like system with:
echo -e '*.yaml\n*.yml' >> .gitignore
Below is an example of retrieving the settings from a settings .yaml file in the same folder / location of the reader.
Code Snippets:
#!/usr/bin/env python3
import yaml
from pathlib import Path
def get_settings():
full_file_path = Path(__file__).parent.joinpath('settings.yaml')
with open(full_file_path) as settings:
settings_data = yaml.load(settings, Loader=yaml.Loader)
return settings_data

Python hashlib.md5 and ejabberd

I am using a python script as an external auth option in ejabberd 2.1.6.
I wanted to start encrypting the clear text passwords that come across in the auth verification, so that they are not being stored in plain text in the backend database. When I add the following code to my python script and restart ejabberd, it hangs:
import hashlib
clear = "barfoo"
salt = "foobar"
hash = hashlib.md5( salt + clear ).hexdigest()
Does hashlib require specific priviledges to run?
When I run it as a normal user (ejabberd) it works without issue. When the python script is run within the external auth of ejabberd it hangs.
I've attempted to have it write out the 'hash' to a file and it never gets there ... if i run it as the 'ejabberd' user, it writes out to file fine.
I've tried to find information about restrictions for using this library on ubuntu without any success. Any ideas?
-sd
** 22.02.2011: Here is the full script adapted from https://git.process-one.net/ejabberd/mainline/blobs/raw/2.1.x/doc/dev.html#htoc8 :
#!/usr/bin/python
import sys
from struct import *
import hashlib
def from_ejabberd():
input_length = sys.stdin.read(2)
(size,) = unpack('>h', input_length)
return sys.stdin.read(size).split(':')
def to_ejabberd(bool):
answer = 0
if bool:
answer = 1
token = pack('>hh', 2, answer)
sys.stdout.write(token)
sys.stdout.flush()
def auth(username, server, password):
clear = "barfoo"
salt = "foobar"
hash = hashlib.md5( salt + clear ).hexdigest()
if (password == hash): return True
else: return False
def isuser(username, server):
return True
def setpass(username, server, password):
return True
while True:
data = from_ejabberd()
success = False
if data[0] == "auth":
success = auth(data[1], data[2], data[3])
elif data[0] == "isuser":
success = isuser(data[1], data[2])
elif data[0] == "setpass":
success = setpass(data[1], data[2], data[3])
to_ejabberd(success)
I have been messing with the same problem. I Can't really track down the problem with openssl binings in _hashlib. Whatever the problem is, i will have to patch python distribution sources. Not really an feasible solution. So i ended up using a pycrypto wrapper for the crypto functions which don't block in this case.
pip install pycrypto
from Crypto.Hash import MD5
m = MD5.new()
m.update("%s%s" % (salt ,clear))
h.hexdigest()
I looked into the hashlib source and while it does not seem to require too much, it does import .so files as modules and one of them hits openssl. It all looks pretty safe but if ejabberd tries to fence itself against calls to 3rd party code (or if you have SELinux or something else to that effect running), stuff can conceivably get weird. I got this in a REPL:
>>> import _md5
>>> _md5.__file__
'/usr/lib/python2.7/lib-dynload/_md5module.so'
Try this on your box and then try putting
_md5 = imp.load_dynamic('_md5', '/usr/lib/python2.7/lib-dynload/_md5module.so')
Or just
import _md5
(with the appropriate path updated to yours) in your code before the offending line and with some trace statement afterwards. Try the same with _hashlib instead of _md5 (hashlib defaults to _hashlib which wraps openssl, but if it doesn't load or doesn't have the needed hash it falls back to _md5, _sha etc.). If it's not the imports that fail/hang then you can try calling _md5.new(salt + clear) and _hashlib.openssl_md5(salt + clear) and see if it's one of them.
If it is the import at fault, then possibly a similar problem was tackled here
I don't know ejabberd, so I can't relate their solution to your problem, unfortunately.
I do have to say it, though: in all python implementations I know, = instead of == in a condition would raise a SyntaxError and that's that - the program would never even enter the main while loop.
hashlib does not require anything special. What means hangs? Where does it hang? Use pdb.set_trace() to step trough the code or use 'strace' or 'ltrace' to investigate API calls.
Try to use logging module, it can help you to watch input and output data, also check scripts permissions, let executing by user which is ejabberd, or for debug just set chmod 777 external.py.

Categories

Resources