Saturday, February 16, 2019

ROT Cipher in Python

Recently I was messing around with some stuff and kept needing a Caesar Cipher deciphered and kept using some not-so-good online ones. So what better thing to do than make one myself. Why even stop at a Caesar Cipher and just make an adjustable ROT tool. So rather than copying one that probably works better and has a nicer design, I made my own.

#!/usr/bin/python3
#
# Made by: Matthew DeSantis
#          www.anarchy46.net

import sys
import os

# Upper case limits
CAP_MIN = ord( 'A' )
CAP_MAX = ord( 'Z' )
# Lower case limits
LOW_MIN = ord( 'a' )
LOW_MAX = ord( 'z' )

# Rotate the characters
def rotate ( offset, c ):
    o = ord( c )
    # Uppercase
    if o >= CAP_MIN and o <= CAP_MAX:
        o = o + offset
        # Loop back to beginning
        if o > CAP_MAX:
            o = o - CAP_MAX + CAP_MIN - 1

        # Loop back to end for backwards
        elif o < CAP_MIN:
            o = CAP_MAX - CAP_MIN + o + 1

    # Lowercase
    elif o >= LOW_MIN and o <= LOW_MAX:
        o = o + offset
        # Loop back to beginning
        if o > LOW_MAX:
            o = o - LOW_MAX + LOW_MIN - 1

        # Loop back to the end
        elif o < LOW_MIN:
            o = LOW_MAX - LOW_MIN + o + 1

    return chr( o )

# Cycle through the string
def cipher ( offset, text ):
    output=''
    for t in text:
        output += rotate( offset, t )
    return output

if __name__ == '__main__':
    try:
        # Max rotation offset
        max_off = CAP_MAX - CAP_MIN + 1
        offset = int( sys.argv[1] )
        # Get the offset from max (like if 27 is in and max is 25, you get 2)
        # Then set positive or negative
        offset = offset and (abs( offset ) - abs( max_off * int( offset / max_off ) )) * (offset / abs( offset )) or offset

        # Text is passed via args
        if len( sys.argv ) > 2:
            text = " ".join( sys.argv[2:] )
            eol = os.linesep

        # Or text is passed via pipe (./rot.py 13 < example.txt)
        elif not sys.stdin.isatty():
            text = "".join( sys.stdin.readlines() )
            eol = ''

        # No text!
        else:
            raise Exception( "Missing text!" )
        print( cipher( int( offset ), text ), end=eol )

    # Not a valid number or the like.
    except ValueError:
        print( "Invalid type." )
        exit( 1 )

    # Catch anything else because oops.
    except Exception as err:
        print( err )
        exit( 1 )


So saving this as rot.py, I can use it for stuff in the terminal:

./rot.py 13 This is a secret message

Or I can use it to decrypt a file:

./rot.py -13 < secret.txt

I tried to make it as flexible as possible. Decryption can be done by using a negative, or you could cipher it negatively, whatever makes you happy. My math for doing the offset is a bit of evil. The idea is to rotate no more than once around while preserving which way around it goes. There are most certainly better ways to do it, but I was feeling "artistic."

No comments:

Post a Comment