This one’s a recent release of mine, I spontaneously had the idea while walking to the shops.

It’s called Rubik’s Crypt due to the Rubik’s cube being the closest real world counterpart, as the inspiration.

The process runs as such:
Create an NxNxN grid where N is the ceiling of the cubed root of the length of your input)

The input is copied to the grid byte by byte, first placed left to right, then top to bottom, then forward to back

for example, in a 3^3 grid the alphabet would be:
[[[97, 98, 99], [100, 101, 102], [103, 104, 105]], [[106, 107, 108], [109, 110, 111], [112, 113, 114]], [[115, 116, 117], [118, 119, 120], [121, 122, _]]]

The final space is empty since there are 26 letters in the alphabet and 27 (3 cubed) spaces available, this blank space can be replaced with any character at all, or left as whitespace, in my version each vacant space is filled with a random char

Once the cube is initialised, you can rotate 2d sections of it independently from the rest, there are functions which rotate only even numbered cross sections, others only odd.

There are functions for mirroring and flipping, as well as swapping every (n2)th point with it’s right hand neighbour where one exists, and one for swapping each (n2+1)th point with it’s right hand neighbour; These functions are reverseable simply by performing them a second time, whereas the rotations can be reversed by using the opposite direction.

For the actual encryption instructions I am producing a SHA256 digest from user password and a salt, then using the resulting bytes as sequences of instructions for encrypting the cube, to decrypt it simply generated the same digest and run the inverse functions (i.e. 90 degrees right instead of left, flips and swaps are their own inverse) in the opposite order

To install it into your command line on windows (and therefore most programming languages with system() popen() or an equivalent) create a batch file rubikscrypt.bat as follows,
@echo off
python c:/mypath/rubikscrypt.py> %*

and make sure to put the batch file in on of the directories in your path (start menu search -> edit the system environment variables -> Environment Variables ->path)

Usage

Options E to encrypt string, D to decrypt string
EF to encrypt file (must also have an output name after the password)
DF to decrypt file (must also have an output name after the password)

rubikscrypt Directive Input Password (OutputFile?)\
e.g.
rubikscrypt E “Encrypt This String” Password
rubikscrypt D “Decrypt This String” Password
rubikscrypt EF filetoencrypt Password outputname
rubikscrypt DF filetodecrypt Password outputname

Security Warning

This is only a Transposition Cipher for shuffling the order of data, it wouldn’t withstand analysis as the frequency distribution of bytes will be the same as the input, it will make the presence and language of the communication obvious (with a large enough section of text).
If you’ve encoded plaintext it could be decoded by looking for anagrams in the form of valid grammar, assuming you use a legible grammar.
Similarly pulse code modulations could be recognized by a normal distribution around a certain value.

This was only a hobby project to scratch an itch.
If you want to use it for practical cryptography you would have to pair it with some sort of position-dependent character substitution before/after/during the shuffling process (transposition).

For real world cryptography please use a well-trusted build of a highly tested standard.

from math import ceil
from random import randint
from hashlib import sha256
import sys

Instructions="""
rubikscrypt Directive Input Password (OutputFile?)\n
e.g.
    rubikscrypt E "Encrypt This String" Password
    rubikscrypt D "Decrypt This String" Password
    rubikscrypt EF filetoencrypt Password outputname
    rubikscrypt DF filetodecrypt Password outputname
"""
def random_char():
    return randint(64,128)

def pack_cube(string,side):
    if type(string)==bytes or type(string)==bytearray:
        input_bytes=string
    else:
        input_bytes=[ord(i) for i in string]
    n=0
    cube=[]
    for x in range(side):
        a=[]
        for y in range(side):
            b=[]
            for z in range(side):
                if n<len(string):
                    b.append(input_bytes[n])
                else:
                    b.append(random_char())
                n+=1
            a.append(b)
        cube.append(a)
    return cube

def unpack_cube(cube,outtype="bytes"):
    side=len(cube)
    s=""
    b=bytearray(b"")
    if outtype=="string":
        for x in range(side):
            for y in range(side):
                for z in range(side):
                    val=cube[x][y][z]
                    s+=chr(val)
        return s
    elif outtype=="bytes":
        for x in range(side):
            for y in range(side):
                for z in range(side):
                    val=cube[x][y][z]
                    b.append(val)
        return b


def rotate_plane_left(plane):
    return list(zip(*plane[::-1]))

def rotate_plane_right(plane):
    return list(zip(*plane))[::-1]

testo=[[1,2,3],
[4,5,6],
[7,8,9]]

leftrot=rotate_plane_left(testo)
lrrot=rotate_plane_right(leftrot)
lrrot=testo
class rubiks_crypt:
    def __init__(self,string):
        self.string=string
        self.length=len(string)
        self.sidelength=ceil(self.length**(1.0/3))
        self.cube=pack_cube(self.string,self.sidelength)
    def switch_rows1(self):
        newcube=[]
        for x in range(self.sidelength):
            a=[]
            for y in range(0,self.sidelength,2):
                    if y+1<self.sidelength:
                        a.append(self.cube[x][y+1])
                    a.append(self.cube[x][y])
            newcube.append(a)
        self.cube=newcube
    def reverse2_even(self):
        for x in range(self.sidelength):
            if x%2==0:
                self.cube[x]=list(self.cube[x])
                self.cube[x].reverse()
    def reverse2_odd(self):
        for x in range(self.sidelength):
            if x%2==1:
                self.cube[x]=list(self.cube[x])
                self.cube[x].reverse()
    def reverse3(self):
        for x in range(self.sidelength):
            for y in range(self.sidelength):
                self.cube[x][y]=list(self.cube[x][y])
                self.cube[x][y].reverse()
    def reverse3_even(self):
        for x in range(self.sidelength):
            for y in range(self.sidelength):
                self.cube[x][y]=list(self.cube[x][y])
                if y%2==0:
                    self.cube[x][y].reverse()
    def reverse3_odd(self):
        for x in range(self.sidelength):
            for y in range(self.sidelength):
                self.cube[x][y]=list(self.cube[x][y])
                if y%2==1:
                    self.cube[x][y].reverse()
    def switch_rows2(self):
        newcube=[]
        for x in range(self.sidelength):
            a=[self.cube[x][0]]
            for y in range(1,self.sidelength,2):
                if y+1<self.sidelength:
                    a.append(self.cube[x][y+1])
                a.append(self.cube[x][y])
            newcube.append(a)
        self.cube=newcube
    def switch_points1(self):
        newcube=[]
        for x in range(self.sidelength):
            a=[]
            for y in range(self.sidelength):
                b=[]
                for z in range(0,self.sidelength,2):
                    if z+1<self.sidelength:
                        b.append(self.cube[x][y][z+1])
                    b.append(self.cube[x][y][z])
                a.append(b)
            newcube.append(a)
        self.cube=newcube

    def switch_points2(self):
        newcube=[]
        for x in range(self.sidelength):
            a=[]
            for y in range(self.sidelength):
                b=[self.cube[x][y][0]]
                for z in range(1,self.sidelength,2):
                    if z+1<self.sidelength:
                        b.append(self.cube[x][y][z+1])
                    b.append(self.cube[x][y][z])
                a.append(b)
            newcube.append(a)
        self.cube=newcube
    def rotate_left(self):#rotates all left
        newcube=[]
        for plane in self.cube:
            newcube.append(rotate_plane_left(plane))
        self.cube=newcube
    def rotate_right(self):#rotates all right
        newcube=[]
        for plane in self.cube:
            newcube.append(rotate_plane_right(plane))
        self.cube=newcube
    def rotate_even_left(self):
        newcube=[]
        for n in range(self.sidelength):
            plane=self.cube[n]
            if n%2==0:
                newcube.append(rotate_plane_left(plane))
            else:
                newcube.append(plane)
        self.cube=newcube
    def rotate_even_right(self):
        newcube=[]
        for n in range(self.sidelength):
            plane=self.cube[n]
            if n%2==0:
                newcube.append(rotate_plane_right(plane))
            else:
                newcube.append(plane)
        self.cube=newcube
    def rotate_odd_left(self):
        newcube=[]
        for n in range(self.sidelength):
            plane=self.cube[n]
            if n%2==1:
                newcube.append(rotate_plane_left(plane))
            else:
                newcube.append(plane)
        self.cube=newcube
    def rotate_odd_right(self):
        newcube=[]
        for n in range(self.sidelength):
            plane=self.cube[n]
            if n%2==1:
                newcube.append(rotate_plane_right(plane))
            else:
                newcube.append(plane)
        self.cube=newcube
    def flip1(self):
        outcube=[]
        for i in range(self.sidelength):
            outcube.append(self.cube[-i])
        self.cube=outcube
    def flip2(self):
        outcube=[]
        for x in range(self.sidelength):
            a=[]
            for y in range(self.sidelength):
                a.append(self.cube[x][-y])
            outcube.append(a)
        self.cube=outcube
    def make_string(self):
        return unpack_cube(self.cube,"string")
    def make_bytes(self):
        return unpack_cube(self.cube,"bytes")
    def encrypt(self,password):
        functions=[self.rotate_left,self.rotate_right,
                   self.rotate_even_left,self.rotate_odd_left,
                   self.rotate_even_right,self.rotate_odd_right,
                   self.switch_rows1,self.switch_rows2,
                   self.switch_points1,self.switch_points2,
                   self.reverse2_even,self.reverse2_odd,self.reverse3,self.reverse3_even,self.reverse3_odd,
                   self.flip1,self.flip2]
        digest=sha256(specialsalt(password,"SALTYSALT and P3PP3R"))
        lf=len(functions)
        hd=digest.hexdigest()
        for i in range(32):
            val=int(hd[i:i+2],16)
            func=functions[val%lf]
            func()
    def decrypt(self,password):
        functions=[self.rotate_right,self.rotate_left,
                   self.rotate_even_right,self.rotate_odd_right,
                   self.rotate_even_left,self.rotate_odd_left,
                   self.switch_rows1,self.switch_rows2,
                   self.switch_points1,self.switch_points2,
                   self.reverse2_even,self.reverse2_odd,self.reverse3,self.reverse3_even,self.reverse3_odd,
                   self.flip1,self.flip2]
        saltedhash=sha256(specialsalt(password,"SALTYSALT and P3PP3R"))
        lf=len(functions)
        hd=saltedhash.hexdigest()
        for n in range(32):
            i=31-n
            val=int(hd[i:i+2],16)
            func=functions[val%lf]
            func()
def specialsalt(password,salt):
    s=""
    lp2=len(password)*2
    ls=len(salt)
    for i in range(lp2):
        if i%2==0:
            s+=password[int(i/2.0)]
        else:
            s+=salt[int(i/2)%ls]
    return s.encode('utf-8')

default_password="DeFaultPasswORD"
def file_to_bytes(Name):
    file=open(Name,'rb')
    bytestream=bytearray(b"")
    while 1:
        val=file.read(1)
        if val==b"":
            break
        bytestream.append(val[0])
    return bytestream
        
def EncryptFile(InFile,OutFile,password=default_password):
    inbytes=file_to_bytes(InFile)
    crypt=rubiks_crypt(inbytes)
    crypt.encrypt(password)
    outbytes=crypt.make_bytes()
    Outfile=open(OutFile,"wb")
    from struct import pack
    Outfile.write(pack("L",len(inbytes)))
    Outfile.write(outbytes)

def DecryptFile(InFile,OutFile,password=default_password):
    inbytes=file_to_bytes(InFile)
    from struct import unpack
    length=unpack("L",inbytes[0:4])[0]
    inbytes=inbytes[4:]
    crypt=rubiks_crypt(inbytes)
    crypt.decrypt(password)
    outbytes=crypt.make_bytes()[:length]
    Outfile=open(OutFile,"wb")
    Outfile.write(outbytes)

def EncryptString(String,password=default_password):
    crypt=rubiks_crypt(String)
    crypt.encrypt(password)
    return crypt.make_string()

def DecryptString(String,password=default_password):
    crypt=rubiks_crypt(String)
    crypt.decrypt(password)
    return crypt.make_string()

if len(sys.argv)==4:
    #first is name, second is string ,3rd is password
    name,directive,string,password=sys.argv
    if directive=="E":
        print(EncryptString(string,password))
    elif directive=="D":
        print(DecryptString(string,password))
    else:
        print(Instructions)
elif len(sys.argv)==5:
    name,directive,inputname,password,outputname=sys.argv
    if directive=="EF":
        print(EncryptFile(inputname,outputname,password))
    elif directive=="DF":
        print(DecryptFile(inputname,outputname,password))
    else:
        print(Instructions)
else:
    print(Instructions)
Back to top