cifrado_vernam_otp.ts

.ts
TypeScript 5.6 | idle

Cifrado Vernam (One-Time Pad) - Visualizacion Interactiva

Este notebook demuestra el Cifrado Vernam paso a paso con visualizacion interactiva de la operacion XOR a nivel de bits. El cifrado Vernam es el unico sistema criptografico demostrado como teoricamente irrompible cuando se usa correctamente (Claude Shannon, 1949).


Principio Fundamental

  • Cada caracter del texto plano se combina con un caracter de la clave mediante la operacion XOR (OR exclusivo)
  • La operacion XOR es su propia inversa: A ^ B ^ B === A
  • Requisito de seguridad perfecta: la clave debe ser aleatoria, de un solo uso y de la misma longitud que el mensaje
  • Implementacion en TypeScript

    [ ]TypeScript
    1// Cifrado Vernam (One-Time Pad) - Implementacion en TypeScript
    2
    3/**
    4 * Cifra un texto plano usando el cifrado Vernam (XOR).
    5 * Cada caracter se combina con el caracter correspondiente
    6 * de la clave mediante la operacion XOR bit a bit.
    7 *
    8 * Requisito: key.length >= plaintext.length para
    9 * seguridad perfecta (One-Time Pad).
    10 */
    11function vernamEncrypt(plaintext: string, key: string): number[] {
    12 const ciphertext: number[] = []
    13 for (let i = 0; i < plaintext.length; i++) {
    14 const keyChar = key[i % key.length]
    15 // XOR entre los code points
    16 const encrypted = plaintext.charCodeAt(i) ^ keyChar.charCodeAt(0)
    17 ciphertext.push(encrypted)
    18 }
    19 return ciphertext
    20}
    21
    22/**
    23 * Descifra un texto cifrado usando el cifrado Vernam.
    24 * XOR es su propia inversa: cipher XOR key = plaintext
    25 */
    26function vernamDecrypt(ciphertext: number[], key: string): string {
    27 const plaintext: string[] = []
    28 for (let i = 0; i < ciphertext.length; i++) {
    29 const keyChar = key[i % key.length]
    30 const decrypted = ciphertext[i] ^ keyChar.charCodeAt(0)
    31 plaintext.push(String.fromCharCode(decrypted))
    32 }
    33 return plaintext.join("")
    34}

    Cifrado y Descifrado

    Ejemplo con texto "HOLA" y clave "CLAVE".

    [ ]TypeScript
    1// Ejemplo de cifrado y descifrado
    2const plaintext: string = "HOLA"
    3const key: string = "CLAVE"
    4
    5console.log(`Texto plano: '${plaintext}'`)
    6console.log(`Clave: '${key}'`)
    7console.log()
    8
    9// Cifrar
    10const cipher: number[] = vernamEncrypt(plaintext, key)
    11const cipherHex: string = cipher
    12 .map((c) => `0x${c.toString(16).padStart(2, "0")}`)
    13 .join(" ")
    14
    15console.log(`Cifrado (dec): [${cipher.join(", ")}]`)
    16console.log(`Cifrado (hex): ${cipherHex}`)
    17console.log()
    18
    19// Descifrar
    20const recovered: string = vernamDecrypt(cipher, key)
    21console.log(`Descifrado: '${recovered}'`)
    22console.log(`Verificacion: ${recovered === plaintext}`)

    Visualizacion Interactiva del XOR

    Ejecuta la celda para ver como cada caracter se cifra bit a bit. Los bits resaltados muestran las posiciones donde los bits de la clave difieren del texto plano.

    [ ]TypeScript
    1// Visualizacion paso a paso del XOR
    2// Ejecuta esta celda para ver la animacion interactiva
    3visualizeVernamXor("HOLA", "CLAVE")

    Tabla de Cifrado Detallada

    Tabla completa mostrando la representacion binaria y la operacion XOR para cada caracter.

    [ ]TypeScript
    1// Tabla detallada de la operacion XOR
    2function printXorTable(plaintext: string, key: string): void {
    3 console.log("Tabla de cifrado XOR caracter a caracter")
    4 console.log("=".repeat(60))
    5
    6 for (let i = 0; i < plaintext.length; i++) {
    7 const p: string = plaintext[i]
    8 const k: string = key[i % key.length]
    9 const pBin: string = p.charCodeAt(0).toString(2).padStart(8, "0")
    10 const kBin: string = k.charCodeAt(0).toString(2).padStart(8, "0")
    11 const xorVal: number = p.charCodeAt(0) ^ k.charCodeAt(0)
    12 const xorBin: string = xorVal.toString(2).padStart(8, "0")
    13 const c: string =
    14 xorVal >= 33 && xorVal <= 126
    15 ? String.fromCharCode(xorVal)
    16 : `0x${xorVal.toString(16).padStart(2, "0")}`
    17
    18 console.log(
    19 ` ${i} | '${p}' ${pBin} | '${k}' ${kBin} | ${xorBin} ${xorVal} -> '${c}'`
    20 )
    21 }
    22}
    23
    24printXorTable("HOLA", "CLAVE")

    Propiedad Fundamental: XOR como Inversa

    La operacion XOR tiene la propiedad de que aplicarla dos veces con la misma clave recupera el valor original. Esto es lo que permite usar la misma operacion tanto para cifrar como para descifrar.

    [ ]TypeScript
    1// Demostracion: XOR es su propia inversa
    2// Esta propiedad fundamental permite usar la misma operacion
    3// para cifrar y descifrar.
    4
    5function demoXorInverse(plaintext: string, key: string): void {
    6 console.log("Propiedad fundamental: A ^ B ^ B === A")
    7 console.log("=".repeat(50))
    8
    9 for (let i = 0; i < plaintext.length; i++) {
    10 const p: string = plaintext[i]
    11 const k: string = key[i % key.length]
    12 const cipherVal: number = p.charCodeAt(0) ^ k.charCodeAt(0)
    13 const decryptVal: number = cipherVal ^ k.charCodeAt(0)
    14
    15 const pBin: string = p.charCodeAt(0).toString(2).padStart(8, "0")
    16 const kBin: string = k.charCodeAt(0).toString(2).padStart(8, "0")
    17 const cBin: string = cipherVal.toString(2).padStart(8, "0")
    18 const dBin: string = decryptVal.toString(2).padStart(8, "0")
    19 const oChr: string = String.fromCharCode(decryptVal)
    20
    21 console.log(`'${p}' (${pBin}) ^ '${k}' (${kBin}) = ${cBin} (cifrado)`)
    22 console.log(` ${cBin} ^ '${k}' (${kBin}) = ${dBin} -> '${oChr}'`)
    23 console.log()
    24 }
    25}
    26
    27demoXorInverse("HOLA", "CLAVE")

    Verificacion del Descifrado

    Tabla paso a paso del proceso inverso: aplicamos XOR del texto cifrado con la clave para recuperar el texto original.

    [ ]TypeScript
    1// Verificacion de descifrado paso a paso
    2function verifyDecryption(plaintext: string, key: string): void {
    3 console.log("Tabla de descifrado (Cifrado ^ Clave = Texto plano)")
    4 console.log("=".repeat(55))
    5
    6 const cipher: number[] = vernamEncrypt(plaintext, key)
    7
    8 cipher.forEach((cVal: number, i: number) => {
    9 const k: string = key[i % key.length]
    10 const cBin: string = cVal.toString(2).padStart(8, "0")
    11 const kBin: string = k.charCodeAt(0).toString(2).padStart(8, "0")
    12 const dVal: number = cVal ^ k.charCodeAt(0)
    13 const dBin: string = dVal.toString(2).padStart(8, "0")
    14 const dChar: string = String.fromCharCode(dVal)
    15
    16 console.log(` ${cBin} ^ '${k}' (${kBin}) = ${dBin} -> '${dChar}'`)
    17 })
    18}
    19
    20verifyDecryption("HOLA", "CLAVE")

    Seguridad: One-Time Pad

    Para que el cifrado Vernam sea teoricamente irrompible, la clave debe ser:

  • Aleatoria - generada con crypto.getRandomValues() (CSPRNG)
  • De un solo uso - nunca reutilizar la clave
  • Igual o mayor longitud que el mensaje
  • [ ]TypeScript
    1// Seguridad: One-Time Pad con clave aleatoria
    2// Usando crypto.getRandomValues() para generar claves seguras
    3
    4function generateOtpKey(length: number): Uint8Array {
    5 // Genera una clave verdaderamente aleatoria (CSPRNG)
    6 const key = new Uint8Array(length)
    7 crypto.getRandomValues(key)
    8 return key
    9}
    10
    11function toHex(bytes: Uint8Array): string {
    12 return Array.from(bytes)
    13 .map((b) => b.toString(16).padStart(2, "0"))
    14 .join("")
    15}
    16
    17const message: string = "SECRETO"
    18const otpKey: Uint8Array = generateOtpKey(message.length)
    19
    20console.log(`Mensaje: '${message}'`)
    21console.log(`Clave OTP (hex): ${toHex(otpKey)}`)
    22
    23// Cifrar con OTP
    24const cipherOtp: Uint8Array = new Uint8Array(
    25 message.split("").map((c, i) => c.charCodeAt(0) ^ otpKey[i])
    26)
    27console.log(`Cifrado (hex): ${toHex(cipherOtp)}`)
    28
    29// Descifrar con OTP
    30const decrypted: string = Array.from(cipherOtp)
    31 .map((c, i) => String.fromCharCode(c ^ otpKey[i]))
    32 .join("")
    33console.log(`Descifrado: '${decrypted}'`)
    34console.log()
    35console.log("NOTA: Si la clave es aleatoria y de un solo uso,")
    36console.log("el cifrado es TEORICAMENTE IRROMPIBLE (Shannon, 1949)")