Reputation: 23
I want to obtain IV from Python backend to Sveltekit and use it to check if the token is the same. I use CryptoJS and pycryptodome. Key is the same.
Python:
key = bytes(os.environ['KEY'], "utf-8")
iv = None
def generate_iv():
rand = os.urandom(4)
return struct.unpack('B' * len(rand), rand)
def encrypt(username, password):
global iv
if iv == None:
iv = generate_iv()
byte_data = b''.join(struct.pack('<I', index) for index in iv)
cipher = AES.new(key, AES.MODE_CBC, iv=byte_data)
data = f"{username}:{password}".encode()
padded_data = pad(data, AES.block_size, style='pkcs7')
encrypted_data = cipher.encrypt(padded_data)
print(base64.b64encode(encrypted_data).decode('utf-8'))
return base64.b64encode(encrypted_data).decode('utf-8')
class IV(APIView):
def get(self, request):
global iv
if iv == None:
iv = generate_iv()
return Response({'iv': f'{iv}'}, status=status.HTTP_200_OK)
JS:
let iv;
let numbers = [];
onMount(async () => {
const response = await fetch("http://127.0.0.1:8000/api/authapi/iv");
const data = await response.json();
const ivNumbers = data.iv.slice(1, -1).split(",").map(Number);
numbers = ivNumbers;
console.log(numbers);
iv = CryptoJS.lib.WordArray.create(new Uint8Array(numbers));
console.log(iv);
});
function encrypt(username, password) {
const key = "pR@QvK3oM&9t#S8X";
const data = `${username}:${password}`;
const encryptedData = CryptoJS.AES.encrypt(
data,
CryptoJS.enc.Utf8.parse(key),
{
mode: CryptoJS.mode.CBC,
padding: CryptoJS.pad.Pkcs7,
iv: iv,
}
);
console.log(encryptedData.toString());
return encryptedData.toString();
}
My API responds with e.g.:
{
"iv": "(246, 72, 5, 222)"
}
But the outcome returns two different values
data: fA6ifpnK8NCLBRi7EYPf4zyDumGFeBwVnZC0vliN06I= (from JS)
token: PAwNhCecq4lJRKizJWCMhknk9gleAPRXV2owj29XD2k= (actual token)
Upvotes: 0
Views: 49
Reputation:
What you are doing is unnecessarily complicated and a vulnerability (since your IV only uses 4 random bytes).
First, the IV for AES is 16 bytes in size: os.urandom(16)
.
For a cross-platform transfer, the IV is most conveniently converted into a string using a binary-to-text encoding such as Base64 (either alone or concatenated with the ciphertext).
CryptoJS provides a Base64 encoder for importing Base64 encoded data.
Sample implementation for Python:
import os
import base64
from Crypto.Cipher import AES
from Crypto.Util.Padding import pad
key = b'pR@QvK3oM&9t#S8X' #bytes(os.environ['KEY'], "utf-8")
iv = None
def generate_iv():
return os.urandom(16)
def encrypt(username, password):
global iv
if iv == None:
iv = generate_iv()
cipher = AES.new(key, AES.MODE_CBC, iv=iv)
data = f"{username}:{password}".encode()
padded_data = pad(data, AES.block_size, style='pkcs7')
encrypted_data = cipher.encrypt(padded_data)
print("IV (Base64 encoded): " + base64.b64encode(iv).decode('utf-8'))
print("Ciphertext (Base64 encoded): " + base64.b64encode(encrypted_data).decode('utf-8'))
return base64.b64encode(encrypted_data).decode('utf-8')
encrypt('username', 'password')
possible output:
IV (Base64 encoded): R22EiVAgUqIo90WfLx2ppQ==
Ciphertext (Base64 encoded): Q5+74TQ7q3y51egBSVVebt6bty22mDolmIjlVldUR1Q=
Sample implementation for JavaScript:
let iv = CryptoJS.enc.Base64.parse("R22EiVAgUqIo90WfLx2ppQ==");
function encrypt(username, password) {
const key = "pR@QvK3oM&9t#S8X";
const data = `${username}:${password}`;
const encryptedData = CryptoJS.AES.encrypt(
data,
CryptoJS.enc.Utf8.parse(key),
{
mode: CryptoJS.mode.CBC,
padding: CryptoJS.pad.Pkcs7,
iv: iv,
}
);
console.log(encryptedData.toString());
return encryptedData.toString();
}
encrypt('username', 'password');
with the output
Q5+74TQ7q3y51egBSVVebt6bty22mDolmIjlVldUR1Q=
in accordance with the output from the Python code.
Upvotes: 1