Framework Thinking - Axon - Encrypt, Decrypt, Hash Code
Here is solutions for Encrypt, Decrypt, Hash Code.
1. Understand the problem
2. Clarify constraints, asks 4 - 5 questions including edge cases.
-
Are all characters lowercase?
✅ Yes, all input characters are guaranteed to be ina–z. -
What if
datais longer thansecret?
✅ The secret must repeat cyclically. -
What if
datais shorter thansecret?
✅ Only the needed portion of the secret is used. -
Is padding required for hashing when block size does not divide the data?
✅ No padding is required for the base version. -
Should decrypt(encrypt(data)) always return the original input?
✅ Yes, this is a fundamental correctness requirement. -
Recognizing that the secret being length 1 is a Caesar Cipher is a cool bit of knowledge indication
-
Ask about whitespace, capital letters, etc
3. Explore examples.
- Example 1 — Basic Encryption
secret = "abc"
data = "abc"
a + a = a (0 + 0 = 0)
b + b = d (1 + 1 = 2)
c + c = e (2 + 2 = 4)
Result = "ade"
- Example 2 — Wrapping
secret = "z"
data = "z"
z = 25, z = 25
25 + 25 = 50 → 50 % 26 = 24 = y
Result = "y"
- Example 3 — Multiple Blocks
secret = "abc"
data = "abcdef"
Block 1: abc + abc → ade
Block 2: def + abc → d+a=e, e+b=g, f+c=h → egh
Result = "adeegh"
- Example 4 — Hash
secret = "abc"
blocks = ["def", "ghi"]
Step 1: abc + def = d+e+f → "dfh"
Step 2: dfh + ghi → d+g=j, f+h=n, h+i=p → "jnp"
Final hash = "jnp"
4. Brainstorm 2 - 3 solutions, naive solution first and optimize later. Explain the key idea of each solution.
class Fuzzcryption:
ALPHABET_SIZE = 26
BASE = ord('a')
def __init__(self, secret: str):
self.secret = secret
self.block_size = len(secret)
# ---------- Core Utilities ----------
def _char_to_int(self, c: str) -> int:
# a -> 1, b -> 2, ..., z -> 26
return ord(c) - self.BASE + 1
def _int_to_char(self, n: int) -> str:
# 1 -> a, 2 -> b, ..., 26 -> z
return chr(n - 1 + self.BASE)
def _bound(self, n: int) -> int:
# Wrap into 1..26
n = n % self.ALPHABET_SIZE
return 26 if n == 0 else n
def _add_chars(self, c1: str, c2: str) -> str:
v1 = self._char_to_int(c1)
v2 = self._char_to_int(c2)
return self._int_to_char(self._bound(v1 + v2))
def _sub_chars(self, c1: str, c2: str) -> str:
v1 = self._char_to_int(c1)
v2 = self._char_to_int(c2)
return self._int_to_char(self._bound(v1 - v2))
# ---------- Encrypt / Decrypt ----------
def encrypt(self, data: str) -> str:
result = []
for i, ch in enumerate(data):
secret_ch = self.secret[i % self.block_size]
result.append(self._add_chars(ch, secret_ch))
return "".join(result)
def decrypt(self, data: str) -> str:
result = []
for i, ch in enumerate(data):
secret_ch = self.secret[i % self.block_size]
result.append(self._sub_chars(ch, secret_ch))
return "".join(result)
# ---------- Hash ----------
def hash(self, data: str) -> str:
acc = self.secret
for i in range(0, len(data), self.block_size):
block = data[i:i + self.block_size]
acc = self._add_strings(acc, block)
return acc
def _add_strings(self, s1: str, s2: str) -> str:
result = []
for c1, c2 in zip(s1, s2):
result.append(self._add_chars(c1, c2))
return "".join(result)
def test_base_case():
""" Easy to ensure a single letter gets encrypted properly """
fc = Fuzzcryption('a')
assert(fc.encrypt('a') == 'b')
assert(fc.encrypt('b') == 'c')
assert(fc.encrypt('c') == 'd')
def test_ceasarcipher():
""" Using only a single letter cipher """
data = 'abcdefghijklmnopqrstuvwxyz'
fc = Fuzzcryption('f')
assert(fc.decrypt(fc.encrypt(data)) == data)
def test_identity():
""" Weird identity property of the algorithm! """
data = 'zzzzzzzz'
fc = Fuzzcryption('z')
assert(fc.encrypt(data) == data)
def test_random():
""" Python has some good builtins for testing this problem, like random and string """
import random
import string
data = ''.join(random.choice(string.ascii_lowercase) for x in range(20))
secret = ''.join(random.choice(string.ascii_lowercase) for x in range(20))
fc = Fuzzcryption(secret)
assert(fc.decrypt(fc.encrypt(data)) == data)
if __name__ == "__main__":
test_base_case()
test_ceasarcipher()
test_identity()
[test_random() for i in range(4)]
-
Time: O(N).
-
Space: O(1) extra space.
5. Implement solutions.
6. Dry run testcases.
- Is this algorithm secure for encryption? Why or Why Not?
- It is not secure because it contains an Identity Property and is Easily Reversible with a very limited state space
- What is the optimal time complexity of this cipher? Space complexity?
- Linear Time complexity ‘O(n)‘ and Constant Space complexity ‘O(1)‘ - We should only need to traverse the data one time end to end
- If you only use a single letter in the secret, what existing cipher does this become?
- Caesar Cipher!
- Is there something you could change or add to the algorithm to make it secure?
- This is open ended, Im not sure if you could or not, but its a fun discussion!