Skip to content

Commit 0ad979b

Browse files
committed
🗓 Sep 29, 2025 9:42:31 PM
✨ sms_encode/decode_multitap
1 parent a6fc5ff commit 0ad979b

File tree

4 files changed

+120
-1
lines changed

4 files changed

+120
-1
lines changed

‎chepy/__version__.py‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
1-
__version__ = "7.4.0" # pragma: no cover
1+
__version__ = "7.5.0" # pragma: no cover
22
__author__ = "@securisec" # pragma: no cover

‎chepy/modules/encryptionencoding.py‎

Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2064,3 +2064,109 @@ def from_zeckendorf(self, space_delim=".", byte_delim="|") -> EncryptionEncoding
20642064

20652065
self.state = bytes(decoded_bytes)
20662066
return self
2067+
2068+
@ChepyDecorators.call_stack
2069+
def sms_encode_multitap(self):
2070+
"""
2071+
Encode text to multi-tap SMS format.
2072+
Example: 'sun' -> '7778866'
2073+
Preserves special characters that aren't on phone keys.
2074+
2075+
Returns:
2076+
Chepy: The Chepy object.
2077+
"""
2078+
KEY_MAP = {
2079+
"2": "ABC",
2080+
"3": "DEF",
2081+
"4": "GHI",
2082+
"5": "JKL",
2083+
"6": "MNO",
2084+
"7": "PQRS",
2085+
"8": "TUV",
2086+
"9": "WXYZ",
2087+
"0": " ",
2088+
}
2089+
REVERSE_MAP = {}
2090+
2091+
for key, letters in KEY_MAP.items():
2092+
for i, letter in enumerate(letters):
2093+
REVERSE_MAP[letter] = key * (i + 1)
2094+
2095+
result = []
2096+
2097+
for char in self._convert_to_str():
2098+
upper_char = char.upper()
2099+
2100+
# Check if character can be encoded
2101+
if upper_char in REVERSE_MAP:
2102+
result.append(REVERSE_MAP[upper_char])
2103+
else:
2104+
# Preserve special characters as-is
2105+
result.append(char)
2106+
2107+
self.state = "".join(result)
2108+
return self
2109+
2110+
@ChepyDecorators.call_stack
2111+
def sms_decode_multitap(self):
2112+
"""
2113+
Decode multi-tap SMS format to text.
2114+
Example: '7778866' -> 'SUN'
2115+
Preserves special characters that aren't numeric keypresses.
2116+
2117+
Returns:
2118+
Chepy: The Chepy object.
2119+
"""
2120+
KEY_MAP = {
2121+
"2": "ABC",
2122+
"3": "DEF",
2123+
"4": "GHI",
2124+
"5": "JKL",
2125+
"6": "MNO",
2126+
"7": "PQRS",
2127+
"8": "TUV",
2128+
"9": "WXYZ",
2129+
"0": " ",
2130+
}
2131+
2132+
result = []
2133+
i = 0
2134+
2135+
encoded = self._convert_to_str()
2136+
2137+
while i < len(encoded):
2138+
current_char = encoded[i]
2139+
2140+
# If it's not a digit, preserve it as-is
2141+
if not current_char.isdigit():
2142+
result.append(current_char)
2143+
i += 1
2144+
continue
2145+
2146+
# If it's not a valid key, preserve it
2147+
if current_char not in KEY_MAP: # pragma: no cover
2148+
result.append(current_char)
2149+
i += 1
2150+
continue
2151+
2152+
letters = KEY_MAP[current_char]
2153+
letters_count = len(letters)
2154+
2155+
# Count consecutive same digits
2156+
count = 0
2157+
while i < len(encoded) and encoded[i] == current_char:
2158+
count += 1
2159+
i += 1
2160+
2161+
# Decode groups of presses, handling wrapping
2162+
while count > 0:
2163+
presses_for_this_letter = count % letters_count
2164+
if presses_for_this_letter == 0:
2165+
presses_for_this_letter = letters_count
2166+
2167+
letter_index = presses_for_this_letter - 1
2168+
result.append(letters[letter_index])
2169+
count -= presses_for_this_letter
2170+
2171+
self.state = "".join(result)
2172+
return self

‎chepy/modules/encryptionencoding.pyi‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,3 +73,5 @@ class EncryptionEncoding(ChepyCore):
7373
def pgp_decrypt(self: EncryptionEncodingT, passphrase: Union[str, bytes], armoured: bool=False) -> EncryptionEncodingT: ...
7474
def to_zeckendorf(self: EncryptionEncodingT, space_delim: Union[str, bytes] = ".", byte_delim: Union[str, bytes] = "|") -> EncryptionEncodingT: ...
7575
def from_zeckendorf(self: EncryptionEncodingT, space_delim: Union[str, bytes] = ".", byte_delim: Union[str, bytes] = "|") -> EncryptionEncodingT: ...
76+
def sms_encode_multitap(self: EncryptionEncodingT) -> EncryptionEncodingT: ...
77+
def sms_decode_multitap(self: EncryptionEncodingT) -> EncryptionEncodingT: ...

‎tests/test_encryptionencoding.py‎

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -938,3 +938,14 @@ def test_zeckendorf():
938938
.o
939939
== b"YnJ1bm5lcns3aDNfZzAxZDNuX3BoMV8wZl96ZWNrZW5kb3JmfQ=="
940940
)
941+
942+
943+
def test_sms_multitap():
944+
assert (
945+
Chepy("!!77773322288777444777733222~~").sms_decode_multitap().o
946+
== b"!!securisec~~".upper()
947+
)
948+
assert (
949+
Chepy("!!securisec~~").sms_encode_multitap().o
950+
== b"!!77773322288777444777733222~~"
951+
)

0 commit comments

Comments
 (0)