#!/usr/bin/env python3

# ========================================================
#    This code is for study only.
#    This code is not optimized.
#    This code is over simplified.
#    This code doesn't respect rules of good coding.
#    This code has duplicate codes:
#        to understanding without complexity.
#    Some values -useless on code- will not be converted.
# ========================================================

import os
import uuid
from cryptography.hazmat.primitives.ciphers import ( Cipher, algorithms, modes )
from cryptography.hazmat.backends import default_backend


def create_item(localtag : bytes, item_value : bytes):
    item_length = len(item_value).to_bytes(2, byteorder='big')
    return localtag + item_length + item_value


# ---------- Cryptographic Framework ------------

# Création des UUID nécessaires
instance_id_uuid = b"\x6d\xfa\x3d\x83\x8a\x80\x45\xfd\xad\xbe\x8a\x65\xdb\xd2\xd1\xa5"
context_sr_uuid  = uuid.uuid4().bytes

# Création de chaque item
item_instance_id = create_item(b'\x3C\x0A', instance_id_uuid)
item_context_sr  = create_item(b'\xFF\xFF', context_sr_uuid)

# Création de Value : concaténation des items
value = item_instance_id + item_context_sr

# Création de Length (format BER long-form-coding : 0x83 donc de 3 octets)
length = b'\x83' + len(value).to_bytes(3, byteorder='big')

# Creation du KLV
cryptographic_framework_ul = b'\x06\x0e\x2b\x34\x02\x53\x01\x01\x0d\x01\x04\x01\x02\x01\x00\x00'
cryptographic_framework = cryptographic_framework_ul + length + value


# ---------- Cryptographic Context --------------

# Création des UUID nécessaires
context_id_uuid = uuid.uuid4().bytes

# Création de chaque item
item_instance_id   = create_item(b'\x3C\x0A', context_sr_uuid)   # uuid du précédent KLV
item_context_id    = create_item(b'\xFF\xFE', context_id_uuid)
item_essence_label = create_item(b'\xFF\xFD', b'\x06\x0e\x2b\x34\x04\x01\x01\x07\x0d\x01\x03\x01\x02\x0c\x01\x00')
item_cipher_algo   = create_item(b'\xFF\xFC', b'\x06\x0e\x2b\x34\x04\x01\x01\x07\x02\x09\x02\x01\x01\x00\x00\x00')
item_mic_algo      = create_item(b'\xFF\xFB', b'\x06\x0e\x2b\x34\x04\x01\x01\x07\x02\x09\x02\x02\x01\x00\x00\x00')
item_key_id        = create_item(b'\xFF\xFA', uuid.uuid4().bytes)

# Création de Value : concaténation de tous les items
value = item_instance_id \
        + item_context_id \
        + item_essence_label \
        + item_cipher_algo \
        + item_mic_algo \
        + item_key_id

# Création de Length (format BER long-form-coding : 0x83 donc de 3 octets)
length = b'\x83' + len(value).to_bytes(3, byteorder='big')

# Creation du KLV
cryptographic_context_ul = b'\x06\x0e\x2b\x34\x02\x53\x01\x01\x0d\x01\x04\x01\x02\x02\x00\x00'
cryptographic_context = cryptographic_context_ul + length + value


# ---------- Encrypted Essence Container ---------

# Cryptographic Content Link
cryptographic_context_link_value = context_id_uuid
cryptographic_context_link_length = len(context_id_uuid).to_bytes(3, byteorder='big')
item_cryptographic_context_link = b'\x83' \
    + cryptographic_context_link_length \
    + cryptographic_context_link_value

# Plaintext Offset
plaintext_offset_value = int(0).to_bytes(8, byteorder='big')
plaintext_offset_length = len(plaintext_offset_value).to_bytes(3, byteorder='big')
item_plaintext_offset = b'\x83' \
    + plaintext_offset_length \
    + plaintext_offset_value

# Source Key
source_key_value = b'\x06\x0e\x2b\x34\x01\x02\x01\x01\x0d\x01\x03\x01\x15\x01\x08\x01'
source_key_length = len(source_key_value).to_bytes(3, byteorder='big')
item_source_key = b'\x83' \
    + source_key_length \
    + source_key_value

# Source Length
source_size = os.path.getsize("essences/JPEG2000/frame.j2c")
source_length_value = source_size.to_bytes(8, byteorder='big')
source_length_length = len(source_length_value).to_bytes(3, byteorder='big')
item_source_length = b'\x83' \
    + source_length_length \
    + source_length_value

# Encrypted Source Value

# Lecture du ficher directement
with open("essences/JPEG2000/frame.j2c", "rb") as file:
    source_content = file.read()

# Notre clef et notre IV
aes_key = b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
iv = os.urandom(16)

# Initialisation du coeur cryptographique
cipher = Cipher(
    algorithms.AES(aes_key),
    modes.CBC(iv),
    backend=default_backend()
)
encryptor = cipher.encryptor()

# Chiffrement de CheckValue
checkvalue = encryptor.update(b'CHUKCHUKCHUKCHUK')

# Création du padding pour arriver à des blocs de 16 octets complet
pad = b'\x00' * (16 - len(source_content) % 16)

# Chiffrement 
encrypted_data = encryptor.update(source_content + pad)

# Creation de item.Value et item.Length
encrypted_source_value = iv + checkvalue + encrypted_data
encrypted_source_length = len(encrypted_source_value).to_bytes(3, byteorder='big')

# Création de l'item complet
item_encrypted_source_value = b'\x83' \
    + encrypted_source_length \
    + encrypted_source_value

# Création de Value
value = item_cryptographic_context_link \
        + item_plaintext_offset \
        + item_source_key \
        + item_source_length \
        + item_encrypted_source_value

# Création du Length
length = b'\x83' + len(value).to_bytes(3, byteorder='big')

# Création du KLV
encrypted_essence_container_ul = b'\x06\x0e\x2b\x34\x02\x04\x01\x01\x0d\x01\x03\x01\x02\x7e\x01\x00'
encrypted_essence_container = encrypted_essence_container_ul + length + value


# ---------- Ecriture dans un fichier -----------
with open("test.mxf", "wb") as file:
    file.write(cryptographic_framework)
    file.write(cryptographic_context)
    file.write(encrypted_essence_container)
