Skip to content

Instantly share code, notes, and snippets.

@DS256
Last active March 14, 2022 21:05
Show Gist options
  • Save DS256/51ed781f6aa01919ba55fef46f619830 to your computer and use it in GitHub Desktop.
Save DS256/51ed781f6aa01919ba55fef46f619830 to your computer and use it in GitHub Desktop.
Encrypting/Decrypting Passwords in Python using External 'C' Function Call

Encrypting/Decrypting Passwords in Python using External 'C' Function Call

Introduction/Problem

I am deploying a stand-alone Raspberry PI based weather station and webcam. This will be visible, known and unattended for days at a time. I will be FTPing to a server and emailing daily status reports over security IP protocols. I was concerned about the unit potentially being stolen. Even in compiled Python text is still clear. There is also an ongoing project to decompile PYC files back to PY.

I posted in the Raspberry PI Forum on my challenge. Look there for some suggestions that were posted on approaches to prevent others from getting access to passwords.

In the end, I decided to use encrypted passwords. However, since at some time my Python 3.9 PYC program may be able to be decompiled, I decided to use a compiled 'C' lanquage decrypt function. I found it challenging in determining how to call and return strings between Python and C. This GIST contains a sample template on how to do this.

The sample encryption method is very simple. The ascii value of each character is added to the provide integer key to encrypt it and subtracted to decrypt it.

References

There is a lot of scattered information around on how to handle the Python/C interaction but no examples of how to call and return a string. In PASSWORD.C I've posted some links people can refer to.

Files Included

There are a number of Python and C programs in this solution:

C Programs

These were developed on a Raspberry PI using gcc 10.2.1. Compile instructions for both are included in the programs

  • password.c - This program asks for the clear password and key. It calls encrypt() to display the encrypted password then decrypt() to decrypt and allow comparison with the original password. The encrypt() function is contained here but the decrypt is included from decrypt.c
  • decrypt.c - This is the decrypt program used in password.c and decrypt_test.py. It is stand-alone so that it can be easily included in the compiled password and as a shared object file (decrypt.so) for decrypt_test.py

Python Script

These were developed on a Raspberry PI using Python 3.9

  • decrypt_test.py - This is sample framework for including the decrypting process in Python. It includes a wrapper Python to contain the specifics of calling a C return.

Running the Example

The following assumes that all files are in the same directory

Setup

From a terminal window

  1. Download the C and PY files
  2. Compile the password.c program to an executable according to the instructions in the program
  3. Compiled the decrypt.c program to decrypt.so according to the instructions in the program

From an editor or Python IDE

  1. Download the decrypt_test.py script.
  2. Find and change the call to decrypt.so to point to the correct directory

Running the Example

  1. From a termina window, run 'password' providing a password and key. I would suggest starting simple and with a low number for the key. If you receive an error of a non-readable encrypted character being created, try a different character or reduce the key number.
  2. Confirm that the decrypted password that comes back from 'password' matches what you entered.
  3. Copy the encrypted password
  4. Run decrypt_test.py. Enter the encrypted password and key. You should then see your original password

Deployment

You can use the decrypt_test.py to craft your implementation. Keep the password program on a device other than the one where your Python program will be running. Run 'password' to create the encrypted passwords you can store as text files in your program.

// DECRYPT() - Simple Example of Decrypting passwords encrypted
// with PASSWORD.C
// Compile DECRYPT() for calling from Python
// gcc -fPIC -shared -o decrypt.so decrypt.c
// CAUTION: Do not embedded PRINTF() in this code. It may cause
// a SEGMENTATION FAULT to be raised when call from Python. Don't
// know why.
#include <stdio.h>
#include <string.h>
const char* decrypt(char encrypted[],int key)
{
int i;
static char decrypted[100];
// Convert encrypted password to password
// by subscripting they key value from ascii value
// of the encrypted key.
for(i=0;i<strlen(encrypted);++i)
{
decrypted[i] = encrypted[i] - key;
}
return decrypted;
}
// END OF DECRYPT TO PULL
import sys
import traceback
# Test harness for DECYRPT() call
# This sample is being provided as one example of Python calling
# a 'C' function and returning a string. It took me a fair amount
# of research and trial and error to get this working. Part of
# the issue was I was very rusty on writing 'C' and forgot what it
# was like to work with a pointer based programming lanquage. I'm
# providing this in return for the many examples I found from others
# Some useful links:
# https://www.journaldev.com/31907/calling-c-functions-from-python
# https://docs.python.org/3/library/ctypes.html
# https://www.programcreek.com/python/example/1243/ctypes.c_char_p
# https://docs.python.org/3/c-api/
# Compile DECRYPT() for calling from Python
# gcc -fPIC -shared -o decrypt.so decrypt.c
# CAUTION: Do not embedded PRINTF() in this code. It will likely cause
# a SEGMENTATION FAULT to be raised when call from Python. Don't
# know why. I created a separate C program to test both the
# encrypt and decrypt functions
#
import ctypes
#-------------------------------------------------------------------
# This Python function is a wrapper around the work that needs to
# be done to correctly call the 'C' function
def decrypt_password(encrypted_password,key):
aux="" # Contains reference info in TRY fails
# Wrap all calling code in TRY
try:
dll = ctypes.CDLL("/home/pi/sample/decrypt.so")
# create byte objects from the string
b_encrypted_pwd = encrypted_password.encode('utf-8')
# send string to c function
# I'm not sure if all the casting is necessary, but this works
dll.decrypt.restype = ctypes.c_char_p # Set return type
t=dll.decrypt(ctypes.c_char_p(b_encrypted_pwd),
ctypes.c_int(key) )
aux=t
# I lost my reference of why decode needs to be used here.
# Likely related to creating the byte object above
return True,t.decode("UTF-8")
except:
exc_type, exc_value, exc_traceback = sys.exc_info()
type=exc_type.__name__
summ = str(traceback.extract_tb(exc_traceback))
summ = summ+". Returned if C pgm called "+str(aux)
print ("DECRYPT_PASSWORD: Exception '"+type+"="+str(exc_value)+"'. Traceback: "+summ)
return False,""
#-------------------------------------------------------------------
def main():
print("This program works with encrypted passwords from PASSWORD.C")
encryp_pwd = input("Enter encrypted password: ")
key = input("Enter key used to encrypt password: ")
ok,pwd=decrypt_password(encryp_pwd,int(key))
print("Decrypt status "+str(ok)+". Decrypted password '"+pwd+"'")
if __name__ == "__main__":
main()
// PASSWORD.C - Sample program to create encrypted passwords
// As a test, it also calls the function to decrypt
// the password that Python will call
// Based on https://c-program-example.com/2012/04/c-program-to-encrypt-and-decrypt-a-password.html
// This program takes a password, encrypts it based on a key then decrypts it.
// The resulting encrypted password may sometimes have non-readable characters.
// Keep the key small. This is a demo only.
// To compiled and create an executable of this program
// gcc -Wall -g -o password password.c -lm
#include <stdio.h>
#include <string.h>
const char* encrypt(char password[],int key)
{
int i;
static char encrypted[100];
// Encrypt password by adding the key to the ascii value of each character
// Issue a warning if the ressulting encrypted password
// is not readable; ASCII(32-126) see https://ascii.cl
for(i=0;i<strlen(password);++i)
{
encrypted[i] = password[i] + key;
if ((encrypted[i] < 32) || (encrypted[i] > 126))
{
printf("Password character [%i]=%c produces an unreadable encryption with key provided\n",i,password[i]);
}
}
return encrypted;
}
// Include the decrypt() function. This is the same one called from
// Python. I know there are better ways to do this with 'C' but this
// is simple
#include "./decrypt.c"
int main()
{
char password[20] ; // Password
int key; // Key value
char en_msg[20]; // Encrypted password
char de_msg[20]; // Decrypted password
printf("Enter the password:\n");
scanf("%s",password);
printf("Enter single digit integer key:\n");
scanf("%d",&key);
printf("Password = %s\n",password);
strcpy(en_msg,encrypt(password,key));
printf("Encrypted Passwrod is = %s\n",en_msg);
strcpy(de_msg,decrypt(en_msg,key));
printf("Dencrypted value = %s\n",de_msg);
printf("Verify the password \n");
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment