Symmetric Encryption

๐Ÿ”’ AES-256 Advanced Encryption Standard

Security Level 256-bit key
Performance โญโญโญโญโญ Excellent
Block Size 128 bits (16 bytes)
Rounds 14 rounds
Hardware Support โœ… AES-NI
Standardization NIST FIPS 197

๐Ÿ“– Overview

AES-256 (Advanced Encryption Standard with 256-bit keys) is the most widely used symmetric encryption algorithm globally. It provides strong security with excellent performance, especially on modern hardware with AES-NI instructions. AES is a substitution-permutation network cipher that operates on 128-bit blocks.

โœจ Key Features

๐Ÿ›๏ธ

Industry Standard

NIST FIPS 197 approved since 2001

โšก

Hardware Acceleration

AES-NI instructions on modern CPUs

๐Ÿ”ง

Multiple Modes

ECB, CBC, CTR, GCM, XTS, and more

โœ…

Proven Security

Extensively analyzed since 1998

๐ŸŒ

Universal Support

Available on all platforms and languages

๐Ÿ›ก๏ธ

Quantum Resistance

128-bit security against Grover's algorithm

๐ŸŽฏ Common Use Cases

๐Ÿ” Data Protection

  • File and disk encryption
  • Database encryption
  • Cloud storage security
  • Backup encryption

๐ŸŒ Network Security

  • TLS/SSL communications
  • VPN tunnels
  • Secure messaging
  • API authentication

๐Ÿ”ง Algorithm Details

๐Ÿ“Š AES-256 Specifications

Key Size
256 bits
32 bytes key length
Block Size
128 bits
16 bytes per block
Rounds
14
14 transformation rounds
Security Level
256-bit post-quantum
128-bit classical

๐Ÿ” Cipher Modes

GCM
Authenticated
Galois/Counter Mode with authentication
CTR
Stream
Counter mode for parallel encryption
CBC
Block
Cipher Block Chaining (legacy)
XTS
Disk
XEX-based tweaked for storage

๐Ÿงฎ Algorithm Structure

AES-256 processes data through 14 rounds of transformations:

Round[i] = AddRoundKey โˆ˜ MixColumns โˆ˜ ShiftRows โˆ˜ SubBytes

Each round applies four operations to transform the 128-bit state.

๐Ÿ’ป Implementation

AES-256 supports multiple modes of operation for different security requirements. Choose the appropriate mode based on your use case.

from metamui_crypto import AES256GCM
import os

# Generate random key
key = os.urandom(32)  # 256 bits

# Create cipher
cipher = AES256GCM(key)

# Encrypt with authentication
plaintext = b"Confidential message"
nonce = os.urandom(12)  # 96-bit nonce
associated_data = b"header info"

ciphertext, tag = cipher.encrypt(
    plaintext,
    nonce,
    associated_data=associated_data
)

# Decrypt and verify
try:
    decrypted = cipher.decrypt(
        ciphertext,
        tag,
        nonce,
        associated_data=associated_data
    )
    print("Authentication successful!")
except ValueError:
    print("Authentication failed!")

CBC Mode with IV

from metamui_crypto import AES256
import os

# Generate key and IV
key = os.urandom(32)
iv = os.urandom(16)  # IV is always 128 bits

# Encrypt with CBC
cipher = AES256.new(key, AES256.MODE_CBC, iv)

# Pad plaintext to multiple of 16 bytes
plaintext = b"Secret message"
padded = AES256.pad(plaintext)

ciphertext = cipher.encrypt(padded)

# Decrypt
decipher = AES256.new(key, AES256.MODE_CBC, iv)
decrypted_padded = decipher.decrypt(ciphertext)
decrypted = AES256.unpad(decrypted_padded)

assert decrypted == plaintext

CTR Mode (Stream Cipher)

from metamui_crypto import AES256
import os

# CTR mode turns AES into a stream cipher
key = os.urandom(32)
nonce = os.urandom(8)  # 64-bit nonce

cipher = AES256.new(key, AES256.MODE_CTR, nonce=nonce)

# Can encrypt any length (no padding needed)
plaintext = b"This can be any length!"
ciphertext = cipher.encrypt(plaintext)

# Decrypt (same operation in CTR)
decipher = AES256.new(key, AES256.MODE_CTR, nonce=nonce)
decrypted = decipher.decrypt(ciphertext)

assert decrypted == plaintext

GCM Mode (Authenticated Encryption)

from metamui_crypto import AES256GCM
import os

# GCM provides authentication
key = os.urandom(32)
cipher = AES256GCM(key)

# Encrypt with authentication
plaintext = b"Confidential data"
nonce = os.urandom(12)  # 96-bit nonce recommended
associated_data = b"metadata"

ciphertext, tag = cipher.encrypt(
    plaintext,
    nonce,
    associated_data=associated_data
)

# Decrypt and verify
try:
    decrypted = cipher.decrypt(
        ciphertext,
        tag,
        nonce,
        associated_data=associated_data
    )
    print("Authentication successful")
except ValueError:
    print("Authentication failed!")

๐Ÿ”ง Implementation Details

AES Round Function

Each round (except the last) consists of:

  1. SubBytes: Non-linear byte substitution using S-box
  2. ShiftRows: Cyclically shift rows
  3. MixColumns: Mix columns using matrix multiplication
  4. AddRoundKey: XOR with round key
def aes_round(state, round_key):
    """Single AES round (simplified)"""
    state = sub_bytes(state)
    state = shift_rows(state)
    state = mix_columns(state)  # Not in final round
    state = add_round_key(state, round_key)
    return state

Key Schedule

AES-256 expands the 256-bit key into 15 round keys:

def key_expansion(key):
    """Expand 256-bit key to round keys"""
    # 32 bytes = 8 words initially
    # Need 60 words total (15 rounds ร— 4 words)
    
    words = []
    # Copy original key
    for i in range(8):
        words.append(key[4*i:4*(i+1)])
    
    # Expand
    for i in range(8, 60):
        temp = words[i-1]
        if i % 8 == 0:
            temp = sub_word(rot_word(temp)) ^ rcon[i//8]
        elif i % 8 == 4:
            temp = sub_word(temp)
        words.append(words[i-8] ^ temp)
    
    return words

Security Properties

  • Avalanche Effect: Single bit change affects entire output
  • No Known Weaknesses: Best attacks barely better than brute force
  • Side-Channel Resistance: Constant-time implementations available
  • Quantum Resistance: 128-bit security against Groverโ€™s algorithm

๐Ÿš€ Advanced Usage

File Encryption with AES-GCM

from metamui_crypto import AES256GCM
import os
import struct

class FileEncryptor:
    def __init__(self, key):
        self.cipher = AES256GCM(key)
    
    def encrypt_file(self, input_path, output_path):
        """Encrypt file with AES-GCM"""
        # Generate random nonce
        nonce = os.urandom(12)
        
        # Read and encrypt file
        with open(input_path, 'rb') as infile:
            plaintext = infile.read()
        
        # Include filename in AAD
        aad = os.path.basename(input_path).encode()
        
        # Encrypt
        ciphertext, tag = self.cipher.encrypt(plaintext, nonce, aad)
        
        # Write encrypted file
        with open(output_path, 'wb') as outfile:
            # Header: nonce_len | nonce | tag | aad_len | aad
            outfile.write(struct.pack('<I', len(nonce)))
            outfile.write(nonce)
            outfile.write(tag)
            outfile.write(struct.pack('<I', len(aad)))
            outfile.write(aad)
            # Ciphertext
            outfile.write(ciphertext)
    
    def decrypt_file(self, input_path, output_path):
        """Decrypt file with AES-GCM"""
        with open(input_path, 'rb') as infile:
            # Read header
            nonce_len = struct.unpack('<I', infile.read(4))[0]
            nonce = infile.read(nonce_len)
            tag = infile.read(16)
            aad_len = struct.unpack('<I', infile.read(4))[0]
            aad = infile.read(aad_len)
            # Read ciphertext
            ciphertext = infile.read()
        
        # Decrypt and verify
        plaintext = self.cipher.decrypt(ciphertext, tag, nonce, aad)
        
        # Write decrypted file
        with open(output_path, 'wb') as outfile:
            outfile.write(plaintext)

Disk Encryption (XTS Mode)

from metamui_crypto import AES256XTS

class DiskEncryption:
    """AES-XTS for disk encryption"""
    
    def __init__(self, key):
        # XTS uses two keys
        self.cipher = AES256XTS(key)  # 512-bit key total
        self.sector_size = 512
    
    def encrypt_sector(self, sector_data, sector_number):
        """Encrypt disk sector"""
        if len(sector_data) != self.sector_size:
            raise ValueError("Invalid sector size")
        
        # Use sector number as tweak
        tweak = sector_number.to_bytes(16, 'little')
        
        return self.cipher.encrypt(sector_data, tweak)
    
    def decrypt_sector(self, encrypted_sector, sector_number):
        """Decrypt disk sector"""
        tweak = sector_number.to_bytes(16, 'little')
        return self.cipher.decrypt(encrypted_sector, tweak)

Key Wrapping (AES-KW)

from metamui_crypto import AES256KW

class KeyManager:
    """Secure key storage using AES Key Wrap"""
    
    def __init__(self, master_key):
        self.kw = AES256KW(master_key)
    
    def wrap_key(self, key_to_wrap):
        """Wrap key for storage"""
        # AES-KW adds integrity check
        wrapped = self.kw.wrap(key_to_wrap)
        return wrapped
    
    def unwrap_key(self, wrapped_key):
        """Unwrap stored key"""
        try:
            key = self.kw.unwrap(wrapped_key)
            return key
        except ValueError:
            raise ValueError("Key integrity check failed")

Parallel Encryption

from metamui_crypto import AES256
import concurrent.futures
import os

class ParallelAES:
    """Parallel AES encryption for large data"""
    
    def __init__(self, key, mode='CTR'):
        self.key = key
        self.mode = mode
        self.chunk_size = 1024 * 1024  # 1MB chunks
    
    def encrypt_parallel(self, data, workers=4):
        """Encrypt data in parallel"""
        if self.mode != 'CTR':
            raise ValueError("Parallel encryption requires CTR mode")
        
        # Split data into chunks
        chunks = []
        for i in range(0, len(data), self.chunk_size):
            chunk = data[i:i + self.chunk_size]
            # Each chunk needs unique counter
            nonce = os.urandom(8)
            counter = i // 16  # Block number
            chunks.append((chunk, nonce, counter))
        
        # Encrypt chunks in parallel
        encrypted_chunks = []
        with concurrent.futures.ThreadPoolExecutor(max_workers=workers) as executor:
            futures = []
            for chunk, nonce, counter in chunks:
                future = executor.submit(
                    self._encrypt_chunk,
                    chunk, nonce, counter
                )
                futures.append(future)
            
            for future in concurrent.futures.as_completed(futures):
                encrypted_chunks.append(future.result())
        
        # Combine results
        return b''.join(encrypted_chunks)
    
    def _encrypt_chunk(self, chunk, nonce, counter):
        """Encrypt single chunk"""
        cipher = AES256.new(
            self.key,
            AES256.MODE_CTR,
            nonce=nonce,
            initial_value=counter
        )
        return cipher.encrypt(chunk)

โšก Performance Optimization

Hardware Acceleration Detection

import platform
import cpuinfo

def detect_aes_ni():
    """Check if AES-NI is available"""
    try:
        info = cpuinfo.get_cpu_info()
        flags = info.get('flags', [])
        return 'aes' in flags
    except:
        # Fallback detection
        if platform.machine() in ['x86_64', 'AMD64']:
            # Most modern x86 CPUs have AES-NI
            return True
        return False

# Use appropriate implementation
if detect_aes_ni():
    from metamui_crypto import AES256NI as AES256
else:
    from metamui_crypto import AES256SW as AES256

Optimized Modes

Mode Speed Use Case
ECB Fastest Never use (insecure)
CTR Very Fast Streaming, parallel
CBC Fast Legacy compatibility
GCM Fast* Authenticated encryption
XTS Fast Disk encryption

*With hardware support

๐Ÿ”’ Security Considerations

Mode Selection

# Bad: ECB mode reveals patterns
# cipher = AES256.new(key, AES256.MODE_ECB)

# Good: Use authenticated encryption
cipher = AES256GCM(key)

# Good: CTR for streaming
cipher = AES256.new(key, AES256.MODE_CTR, nonce=nonce)

IV/Nonce Management

class NonceManager:
    """Secure nonce management"""
    
    def __init__(self):
        self.counter = 0
        self.prefix = os.urandom(8)
    
    def get_nonce(self):
        """Get unique nonce"""
        # Combine random prefix with counter
        nonce = self.prefix + self.counter.to_bytes(4, 'big')
        self.counter += 1
        
        if self.counter >= 2**32:
            # Reset with new prefix
            self.prefix = os.urandom(8)
            self.counter = 0
        
        return nonce

Key Derivation

from metamui_crypto import HKDF, AES256GCM

def derive_encryption_key(master_key, context):
    """Derive AES key from master key"""
    # Use HKDF for key derivation
    salt = b"aes-encryption-v1"
    
    derived_key = HKDF(
        master_key,
        salt=salt,
        info=context.encode(),
        length=32  # 256 bits for AES-256
    )
    
    return derived_key

# Different keys for different purposes
file_key = derive_encryption_key(master_key, "file-encryption")
comm_key = derive_encryption_key(master_key, "communication")

โš ๏ธ Common Pitfalls

1. ECB Mode Patterns

# Bad: ECB mode shows patterns
# Two identical blocks encrypt to same ciphertext
plaintext = b"Hello World!!!!!" * 2  # Repeated block
cipher = AES256.new(key, AES256.MODE_ECB)
ciphertext = cipher.encrypt(plaintext)
# ciphertext[:16] == ciphertext[16:32]  # Reveals pattern!

# Good: Use CBC or CTR
cipher = AES256.new(key, AES256.MODE_CBC, iv)

2. IV Reuse

# Bad: Reusing IV
# iv = b'0' * 16  # Never do this!

# Good: Random IV for each encryption
iv = os.urandom(16)

3. Padding Oracle Attacks

# Bad: Revealing padding errors
# try:
#     plaintext = cipher.decrypt(ciphertext)
#     unpadded = unpad(plaintext)
# except PaddingError:
#     return "Padding error"  # Leaks information!

# Good: Use authenticated encryption
try:
    plaintext = aes_gcm.decrypt(ciphertext, tag, nonce)
except ValueError:
    return "Decryption failed"  # Generic error

๐Ÿ”„ Migration Guide

From 3DES/Blowfish

# Old: 3DES
# from Crypto.Cipher import DES3
# cipher = DES3.new(key, DES3.MODE_CBC, iv)

# New: AES-256
from metamui_crypto import AES256
cipher = AES256.new(key, AES256.MODE_CBC, iv)

# Benefits:
# - Much faster
# - Stronger security
# - Hardware acceleration

To Authenticated Encryption

# Old: AES-CBC with separate HMAC
# ciphertext = aes_cbc.encrypt(plaintext)
# mac = hmac.new(key, ciphertext, sha256).digest()

# New: AES-GCM (integrated authentication)
ciphertext, tag = aes_gcm.encrypt(plaintext, nonce)

# Benefits:
# - Single operation
# - Better performance
# - Prevents implementation errors

๐Ÿ“š Resources

๐Ÿ“œ

NIST FIPS 197

Official AES Specification

๐Ÿ”’

NIST SP 800-38A

Block Cipher Modes of Operation

๐Ÿ”

NIST SP 800-38D

GCM Mode Specification

๐Ÿ›ก๏ธ

Security Analysis

Detailed security properties

โšก

AES-NI Instructions

Hardware acceleration guide