.. DO NOT EDIT. .. THIS FILE WAS AUTOMATICALLY GENERATED BY SPHINX-GALLERY. .. TO MAKE CHANGES, EDIT THE SOURCE PYTHON FILE: .. "api/gallery/crypto/one_time_pad/plot_01_one_time_pad.py" .. LINE NUMBERS ARE GIVEN BELOW. .. only:: html .. note:: :class: sphx-glr-download-link-note :ref:`Go to the end ` to download the full example code or to run this example in your browser via JupyterLite. .. rst-class:: sphx-glr-example-title .. _sphx_glr_api_gallery_crypto_one_time_pad_plot_01_one_time_pad.py: The one-time pad and perfect secrecy (Vernam 1917, Shannon 1949) ================================================================ Vernam's teleprinter cipher added a key tape to the message, character by character. Shannon later proved that if the key is truly random, as long as the message, and used once, the ciphertext reveals *nothing*: every plaintext of that length is equally consistent with it. What to look for ---------------- For one ciphertext, find a key that "decrypts" it to any message you like: that is perfect secrecy. Then reuse a key for two messages and watch the pad cancel out, leaving the XOR of the plaintexts. The history behind this experiment: :doc:`/history/crypto_breakthroughs`. .. GENERATED FROM PYTHON SOURCE LINES 21-23 Encrypt and decrypt with the same operation ------------------------------------------- .. GENERATED FROM PYTHON SOURCE LINES 23-34 .. code-block:: Python import matplotlib.pyplot as plt import numpy as np import blockchainkit as bk message = b"ATTACK AT DAWN" key = bytes([23, 199, 5, 88, 241, 7, 130, 61, 17, 250, 92, 3, 144, 66]) # Fixed for the lesson. ciphertext = bk.crypto.one_time_pad(message, key) assert bk.crypto.one_time_pad(ciphertext, key) == message print("ciphertext:", ciphertext.hex()) .. rst-class:: sphx-glr-script-out .. code-block:: none ciphertext: 56935119b24ca27c45da1842c70c .. GENERATED FROM PYTHON SOURCE LINES 35-39 Every plaintext is possible --------------------------- For each candidate there is exactly one key: candidate XOR ciphertext. Without the key, an eavesdropper cannot rule any of them out. .. GENERATED FROM PYTHON SOURCE LINES 39-44 .. code-block:: Python for candidate in (b"ATTACK AT DAWN", b"RETREAT AT TEN", b"HOLD POSITION!"): fitting_key = bk.crypto.xor_bytes(ciphertext, candidate) assert bk.crypto.one_time_pad(ciphertext, fitting_key) == candidate print(candidate.decode(), "<- key", fitting_key.hex()[:12], "...") .. rst-class:: sphx-glr-script-out .. code-block:: none ATTACK AT DAWN <- key 17c70558f107 ... RETREAT AT TEN <- key 04d6054bf70d ... HOLD POSITION! <- key 1edc1d5d921c ... .. GENERATED FROM PYTHON SOURCE LINES 45-49 Reusing the pad breaks it ------------------------- The "two-time pad": XORing two ciphertexts removes the key. Soviet reuse of one-time pads is what the Venona project exploited. .. GENERATED FROM PYTHON SOURCE LINES 49-55 .. code-block:: Python second = b"RETREAT AT TEN" leak = bk.crypto.xor_bytes(ciphertext, bk.crypto.one_time_pad(second, key)) assert leak == bk.crypto.xor_bytes(message, second) guess = bk.crypto.xor_bytes(leak, b"ATTACK AT DAWN") # A guessed first message... assert guess == second # ...reveals the second one exactly. .. GENERATED FROM PYTHON SOURCE LINES 56-58 Ciphertext bytes are uniform; plaintext bytes are not ----------------------------------------------------- .. GENERATED FROM PYTHON SOURCE LINES 58-68 .. code-block:: Python rng = np.random.default_rng(1949) text = b"the quick brown fox jumps over the lazy dog " * 200 pad = rng.integers(0, 256, len(text), dtype=np.uint8).tobytes() fig, axes = plt.subplots(1, 2, figsize=(10, 3.5), sharey=True) axes[0].hist(list(text), bins=64, range=(0, 256), color="#64748b") axes[0].set(title="Plaintext byte values", xlabel="byte") axes[1].hist(list(bk.crypto.one_time_pad(text, pad)), bins=64, range=(0, 256), color="#2563eb") axes[1].set(title="One-time-pad ciphertext", xlabel="byte") fig.tight_layout() .. image-sg:: /api/gallery/crypto/one_time_pad/images/sphx_glr_plot_01_one_time_pad_001.png :alt: Plaintext byte values, One-time-pad ciphertext :srcset: /api/gallery/crypto/one_time_pad/images/sphx_glr_plot_01_one_time_pad_001.png :class: sphx-glr-single-img .. GENERATED FROM PYTHON SOURCE LINES 69-74 Exercise -------- The pad needs as much key as message. Count the key bytes needed to encrypt one gigabyte, and explain why Diffie-Hellman (1976) was needed to agree on short keys instead. Why does a repeated *short* key (a Vigenere cipher) fail? .. rst-class:: sphx-glr-timing **Total running time of the script:** (0 minutes 0.295 seconds) .. _sphx_glr_download_api_gallery_crypto_one_time_pad_plot_01_one_time_pad.py: .. only:: html .. container:: sphx-glr-footer sphx-glr-footer-example .. container:: lite-badge .. image:: images/jupyterlite_badge_logo.svg :target: ../../../../lite/lab/index.html?path=api/gallery/crypto/one_time_pad/plot_01_one_time_pad.ipynb :alt: Launch JupyterLite :width: 150 px .. container:: sphx-glr-download sphx-glr-download-jupyter :download:`Download Jupyter notebook: plot_01_one_time_pad.ipynb ` .. container:: sphx-glr-download sphx-glr-download-python :download:`Download Python source code: plot_01_one_time_pad.py ` .. container:: sphx-glr-download sphx-glr-download-zip :download:`Download zipped: plot_01_one_time_pad.zip ` .. only:: html .. rst-class:: sphx-glr-signature `Gallery generated by Sphinx-Gallery `_