PepeePL
PepeePL

Reputation: 23

Get IV from Django backend

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

Answers (1)

user21105343
user21105343

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

Related Questions