1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
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
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
| from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.primitives.kdf.hkdf import HKDF
import math
class FF1Algorithm:
def __init__(self, key, tweak=b""):
self.key = key
self.tweak = tweak
self.radix = 10 # Para dígitos decimales
def encrypt(self, plaintext):
"""Cifrar usando FF1"""
n = len(plaintext)
u = n // 2
v = n - u
A = plaintext[:u]
B = plaintext[u:]
# 10 rondas
for i in range(10):
# Calcular C
C = self.calculate_C(A, i, u, v)
# Calcular P
P = self.calculate_P(C, i)
# Calcular Y
Y = self.calculate_Y(P)
# Calcular Z
Z = self.calculate_Z(Y, B)
# Actualizar A y B
A_new = B
B_new = self.subtract_mod_radix(A, Z)
A = A_new
B = B_new
return A + B
def decrypt(self, ciphertext):
"""Descifrar usando FF1"""
n = len(ciphertext)
u = n // 2
v = n - u
A = ciphertext[:u]
B = ciphertext[u:]
# 10 rondas en orden inverso
for i in range(9, -1, -1):
# Calcular C
C = self.calculate_C(A, i, u, v)
# Calcular P
P = self.calculate_P(C, i)
# Calcular Y
Y = self.calculate_Y(P)
# Calcular Z
Z = self.calculate_Z(Y, B)
# Actualizar A y B
B_new = A
A_new = self.add_mod_radix(B, Z)
A = A_new
B = B_new
return A + B
def calculate_C(self, A, i, u, v):
"""Calcular C según FF1"""
# Implementación simplificada
return f"{i:02d}{A}{u:02d}{v:02d}"
def calculate_P(self, C, i):
"""Calcular P según FF1"""
# Implementación simplificada
return f"{C}{self.tweak.decode()}{i:02d}"
def calculate_Y(self, P):
"""Calcular Y usando AES"""
cipher = Cipher(algorithms.AES(self.key), modes.ECB())
encryptor = cipher.encryptor()
# Padding para múltiplo de 16 bytes
padded_P = P.encode().ljust(16, b'\x00')
encrypted = encryptor.update(padded_P) + encryptor.finalize()
return encrypted
def calculate_Z(self, Y, B):
"""Calcular Z"""
# Implementación simplificada
return Y[:len(B)]
def add_mod_radix(self, a, b):
"""Suma módulo radix"""
result = ""
carry = 0
for i in range(len(a) - 1, -1, -1):
sum_digits = int(a[i]) + int(b[i]) + carry
result = str(sum_digits % self.radix) + result
carry = sum_digits // self.radix
return result
def subtract_mod_radix(self, a, b):
"""Resta módulo radix"""
result = ""
borrow = 0
for i in range(len(a) - 1, -1, -1):
diff = int(a[i]) - int(b[i]) - borrow
if diff < 0:
diff += self.radix
borrow = 1
else:
borrow = 0
result = str(diff) + result
return result
# Ejemplo de uso
key = b'1234567890123456' # 16 bytes para AES-128
fpe = FF1Algorithm(key, tweak=b"tweak")
# Cifrar número de tarjeta de crédito
credit_card = "4111111111111111"
encrypted = fpe.encrypt(credit_card)
print(f"Tarjeta cifrada: {encrypted}")
# Descifrar
decrypted = fpe.decrypt(encrypted)
print(f"Tarjeta descifrada: {decrypted}")
|