Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

encrypt a string to obtain a fixed length

let's suppose I have the following possible values of a string:

exp="110"
exp="110-120"
exp="110-120-211"

actually, this is a GET parameter obtained from the URL. exp can become very large so I want to make it shorter(encrypt it). Not because of security reasons, but because I don't want it to be long and ugly.

So I want to encrypt exp to become a short string with a fixed length of let's say 15. Is it possible somehow? Something like this:

encrypt("110")     results in "Ax1234B"
encrypt("110-120") results in "85xHdjX"

I am using python btw

EDIT

forgot to mention: I also need a decrypt function to be available. Moreover, I would prefer solutions from the standard python library without having to install new packages.

like image 821
Mihai Zamfir Avatar asked Sep 16 '25 03:09

Mihai Zamfir


2 Answers

How long are your strings getting? If they're sufficiently long, you could compress them using zlib (or another compression module in the standard libary) and then run base64 on it.

>>> z = base64.encodestring(zlib.compress("123"))
>>> print z 
eJwzNDIGAAEtAJc=
>>> zlib.decompress(base64.decodestring(z)) 
'123'

This isn't going to shrink your strings unless they're pretty long, though (in my tests about 36 characters in the original string). You're also not getting a fixed length, but I don't believe there's any way to achieve that.

like image 122
dano Avatar answered Sep 18 '25 16:09

dano


If you want to convert urls into a fixed length strings; you could use a hash function and a database to be able to retrieve the url given its hash:

import base64
import hashlib
import sqlite3

db = sqlite3.connect('urls.sqlite3')
db.execute('''CREATE TABLE IF NOT EXISTS urls
              (hash BLOB PRIMARY KEY, url TEXT)''')

def shorten(url):
    h = sqlite3.Binary(hashlib.sha256(url.encode('ascii')).digest())
    with db:
        db.execute('INSERT OR IGNORE INTO urls VALUES (?, ?)', (h, url))
    return base64.urlsafe_b64encode(h).decode('ascii')

def geturl(shortened_url):
    h = sqlite3.Binary(base64.urlsafe_b64decode(shortened_url.encode('ascii')))
    with db:
        url = db.execute('SELECT url FROM urls WHERE hash=?', (h,)).fetchone()
    if url is None:
        raise KeyError(shortened_url)
    return url[0]

Example

urls = ["110", "110-120", "110-120-211"]
width = max(map(len, urls))
for url in urls:
    slug = shorten(url)
    assert url == geturl(slug)
    print('{url:{width}} -> {slug}'.format(**vars()))

Output

110         -> m9sq9nmSBKKZxgOZS45ADksf1iXv23QGbMhp_uQsnfM=
110-120     -> aKGvjidWggSkQ1wBnZoi5f67KlUS1pvoVyhX8Rd04P0=
110-120-211 -> C8LD7lCh5Tm8XCoWJep9OAfSnMikLU5lgQChe-wfQho=

The output always has the same length however long (or short) input urls are.

For a sufficiently long hash with a good algorithm the probability of collision (different urls producing the same hash) is very low for any practical number of url hashes generated.

like image 40
jfs Avatar answered Sep 18 '25 16:09

jfs