nomoresecrets

Crown-OTP keystream gen time, attack speed and KPA

Mar 20th, 2024
73
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Java 12.15 KB | None | 0 0
  1. package fields.tests;
  2.  
  3. import java.math.BigInteger;
  4. import java.nio.charset.StandardCharsets;
  5. import java.util.ArrayList;
  6. import java.util.List;
  7.  
  8. import org.junit.jupiter.api.Test;
  9.  
  10. import fields.floatingpoint.FiniteRealVectorSpace;
  11. import fields.floatingpoint.Reals;
  12. import fields.floatingpoint.Reals.Real;
  13. import fields.integers.Integers;
  14. import fields.integers.Integers.IntE;
  15. import fields.vectors.RealLattice;
  16. import fields.vectors.SubRealInnerProductSpace;
  17. import fields.vectors.Vector;
  18.  
  19.  
  20. class CrownOTPTest {
  21.  
  22.     @Test
  23.     void Test1WhatSizePlaintextTakes10SecondsToEncrypt() {
  24.         System.out.println("\n-------------------------------------------------------------------------");
  25.         System.out.println("TEST 1: Test what size plaintext takes unacceptable 10 seconds to encrypt");
  26.         System.out.println("-------------------------------------------------------------------------");
  27.        
  28.         int key_bits = 256;
  29.         int key_stream_len_bytes = 128;
  30.         long KeystreamGenTimeMS;
  31.         while (true) {
  32.             System.out.printf("Testing %s-byte keystream...", key_stream_len_bytes);
  33.  
  34.             // Generate key
  35.             int key_stream_len_bits = 8 * key_stream_len_bytes;
  36.             Reals r = Reals.r(key_stream_len_bits);
  37.             Integers z = Integers.z();
  38.             IntE key = z.getRandomElement(z.power(z.getInteger(2), key_bits));
  39.  
  40.             // Generate keystream and measure time required to generate it
  41.             long KeystreamGenStart = System.nanoTime();
  42.             Real sqrt = r.positiveSqrt(r.getInteger(key));
  43.             Real keystream = sqrt.fractionalPart();
  44.             long KeystreamGenEnd = System.nanoTime();
  45.             KeystreamGenTimeMS = (KeystreamGenEnd - KeystreamGenStart) / 1000000;
  46.             System.out.printf("%d ms\n", KeystreamGenTimeMS);
  47.  
  48.             if (KeystreamGenTimeMS > 10000) {
  49.                 break;
  50.             } else {
  51.                 key_stream_len_bytes *= 2;
  52.             }
  53.  
  54.         }
  55.         System.out.printf("\nEncrypting just %s kilobytes takes %d seconds.\n",
  56.                 key_stream_len_bytes/1024,
  57.                 KeystreamGenTimeMS/1000);
  58.     }
  59.  
  60.    
  61.     @Test
  62.     void Test2WhenKeystreamGenBecomesSlowerThanBreakingIt() {
  63.         System.out.println("\n------------------------------------------------------------------------------");
  64.         System.out.println("TEST 2: Test when keystream generation becomes slower than breaking the cipher");
  65.         System.out.println("------------------------------------------------------------------------------");
  66.  
  67.         int key_bits = 256;
  68.         System.out.printf("\nKey size: %d bytes\n", key_bits/8);
  69.  
  70.         for (int key_stream_len_bytes = 128; key_stream_len_bytes < 1024 * 1024; key_stream_len_bytes *= 2) {
  71.  
  72.             // Generate key
  73.             int key_stream_len_bits = 8 * key_stream_len_bytes;
  74.             Reals r = Reals.r(key_stream_len_bits);
  75.             Integers z = Integers.z();
  76.             IntE key = z.getRandomElement(z.power(z.getInteger(2), key_bits));
  77.  
  78.             // Generate keystream and measure time required to generate it
  79.             long KeystreamGenStart = System.nanoTime();
  80.             Real sqrt = r.positiveSqrt(r.getInteger(key));
  81.             Real keystream = sqrt.fractionalPart();
  82.             long KeystreamGenEnd = System.nanoTime();
  83.             long KeystreamGenTimeMS = (KeystreamGenEnd - KeystreamGenStart) / 1000000;
  84.  
  85.             // Break key from keystream and measure time required to break it
  86.             long BreakRNGStart = System.nanoTime();
  87.             IntE reconstructed = breakCrownOTP(keystream, 3 * key_bits, key_bits);
  88.             long BreakRNGEnd = System.nanoTime();
  89.             long BreakRNGTimeMS = (BreakRNGEnd - BreakRNGStart) / 1000000;
  90.            
  91.             if (key.equals(reconstructed)) {
  92.                 if (KeystreamGenTimeMS > BreakRNGTimeMS) {
  93.                     System.out.printf("\nBreaking the cipher is faster than encrypting messages,\n" +
  94.                             "when the length of sent message is longer than %d bytes.\n", key_stream_len_bytes);
  95.                     break;
  96.                 }
  97.             }
  98.  
  99.         }
  100.     }
  101.  
  102.    
  103.     @Test
  104.     void Test3KPAAndExistentialForgeryWithoutOffset() {
  105.         System.out.println("\n-----------------------------------");
  106.         System.out.println("TEST 3: KPA and existential forgery");
  107.         System.out.println("-----------------------------------");
  108.  
  109.         int key_bits = 256;
  110.  
  111.         System.out.println("\nSender:\n");
  112.  
  113.         // Define secret plaintext message
  114.         String plaintext = "Hello Linda, this is Robert Grant, the CEO of the company." +
  115.                 " Please transfer 1,000 ETH to account 0x0123456789abcdef0123456789abcdef0123456";
  116.         byte[] plaintextBytes = plaintext.getBytes(StandardCharsets.UTF_8);
  117.  
  118.         System.out.printf("  Original Plaintext: %s\n", plaintext);
  119.  
  120.         // Generate key
  121.         int key_stream_bit_len = 8 * plaintext.length();
  122.         Reals r = Reals.r(key_stream_bit_len);
  123.         Integers z = Integers.z();
  124.         IntE key = z.getRandomElement(z.power(z.getInteger(2), key_bits));
  125.        
  126.         // Generate the keystream
  127.         Real sqrt = r.positiveSqrt(r.getInteger(key));      
  128.         String discardedOffset = String.valueOf(sqrt).split("\\.")[0];
  129.         Real fractionalPart = sqrt.fractionalPart();
  130.         String keystream = String.valueOf(fractionalPart).split("\\.")[1];
  131.         byte[] keystreamBytes = new BigInteger(keystream).toByteArray();
  132.  
  133.         // Encrypt the plaintext by XORing it with the keystream
  134.         byte[] ciphertextBytes = xorByteArrays(plaintextBytes, keystreamBytes);
  135.  
  136.         // Output
  137.         String dashes = new String(new char[1096]).replace('\0', '-');
  138.         System.out.printf("  Original key:       %s\n", key);
  139.         System.out.printf("  Discarded KS:       %s\n", discardedOffset);
  140.         System.out.printf("  Original Base10 KS: %s\n\n", keystream);
  141.        
  142.         printByteArray(plaintextBytes, "  PT");
  143.         printByteArray(keystreamBytes, "  KS");
  144.         System.out.printf("  XOR %s\n", dashes);
  145.         printByteArray(ciphertextBytes, "  CT");
  146.        
  147.         // -------------------------------------------------------------------------------------------------------------
  148.  
  149.         System.out.println("\nMITM Attacker:\n");
  150.  
  151.         String knownPlaintext = "Hello Linda, this is Robert Grant, the CEO of the company.";
  152.         System.out.printf("  Known plaintext:                       '%s'.\n", knownPlaintext);
  153.  
  154.         byte[] knownPlaintextBytes = knownPlaintext.getBytes(StandardCharsets.UTF_8);
  155.         byte[] recoveredKeystreamBytes = xorByteArrays(ciphertextBytes, knownPlaintextBytes);
  156.  
  157.         String CTBits = byteArrayToBinaryString(ciphertextBytes);
  158.         String recoveredOnesAndZeroes = byteArrayToBinaryString(recoveredKeystreamBytes);
  159.  
  160.         // Concatenate recovered keystream with trailing zeroes to match ciphertext length:
  161.         int numberOfTrailingZeroes = CTBits.length() - recoveredOnesAndZeroes.length();
  162.         recoveredOnesAndZeroes += "0".repeat(Math.max(0, numberOfTrailingZeroes));
  163.  
  164.         BigInteger partiallyCorrectRecoveredKSInt = new BigInteger(recoveredOnesAndZeroes, 2);
  165.         String partialKPAKeystream = String.valueOf(partiallyCorrectRecoveredKSInt).substring(0, 90);
  166.         String partialKPAFractional = String.format("0.%s", partialKPAKeystream);
  167.  
  168.         System.out.printf("  KPA Partial Base10 keystream recovery: %s\n", partialKPAKeystream);
  169.  
  170.         // Break the cipher by recovering key from partial keystream
  171.         IntE reconstructedKey = breakCrownOTP(r.fromString(partialKPAFractional), 4 * key_bits, key_bits);
  172.  
  173.         // Reconstruct the full keystream
  174.         Reals r_attacker = Reals.r(key_stream_bit_len);
  175.         Real sqrt_attacker = r_attacker.positiveSqrt(r.getInteger(reconstructedKey));
  176.         Real fractionalPartReconstruct = sqrt_attacker.fractionalPart();
  177.         String keyStreamReconstruct = String.valueOf(fractionalPartReconstruct).split("\\.")[1];
  178.         byte[] keyStreamReconstructBytes = new BigInteger(keyStreamReconstruct).toByteArray();
  179.         System.out.printf("  Full keystream recovery with lattices: %s\n", keyStreamReconstruct);
  180.  
  181.         // Decrypt the message
  182.         byte[] recoveredPlaintextBytes = xorByteArrays(keyStreamReconstructBytes, ciphertextBytes);
  183.         String recoveredPlaintext = new String(recoveredPlaintextBytes, StandardCharsets.UTF_8);
  184.         System.out.printf("  Recovered Plaintext:                   '%s'\n", recoveredPlaintext);
  185.  
  186.         String newPlaintext = "Hello Linda, this is Robert Grant, the CEO of the company." +
  187.                 " Please transfer 1,000,000,000 ETH to 0xfffffffffffffffffffffffffffffffffffffff";
  188.         System.out.printf("  New, forged plaintext:                 '%s'\n", newPlaintext);
  189.         byte[] newPlaintextBytes = newPlaintext.getBytes(StandardCharsets.UTF_8);
  190.         byte[] newCiphertextBytes = xorByteArrays(newPlaintextBytes, keyStreamReconstructBytes);
  191.  
  192.         printByteArray(newPlaintextBytes, "\n  NEW PT");
  193.         printByteArray(keyStreamReconstructBytes, "  REC KS");
  194.         System.out.printf("  XOR %s\n", dashes);
  195.         printByteArray(newCiphertextBytes, "  NEW CT");
  196.  
  197.         // -------------------------------------------------------------------------------------------------------------
  198.  
  199.         System.out.println("\nRecipient:\n");
  200.  
  201.         byte[] finalPlaintextBytes = xorByteArrays(newCiphertextBytes, keystreamBytes);
  202.         String finalPlaintext = new String(finalPlaintextBytes, StandardCharsets.UTF_8);
  203.        
  204.         printByteArray(newCiphertextBytes, "  CT");
  205.         printByteArray(keystreamBytes, "  KS");
  206.         System.out.printf("  XOR %s\n", dashes);
  207.         printByteArray(finalPlaintextBytes, "  PT");
  208.        
  209.         System.out.printf("\n  Recipient's plaintext: '%s'\n", finalPlaintext);
  210.     }
  211.  
  212.  
  213.     private static String byteArrayToBinaryString(byte[] byteArray) {
  214.         StringBuilder BinaryString = new StringBuilder();
  215.         for (byte b : byteArray) {
  216.             String binary = String.format("%8s", Integer.toBinaryString(b & 0xFF)).replace(' ', '0');
  217.             BinaryString.append(binary);
  218.         }
  219.         return String.valueOf(BinaryString);
  220.     }
  221.  
  222.    
  223.     private static void printByteArray(byte[] keystreamBytes, String name) {
  224.         StringBuilder keystreamOnesZeroes = new StringBuilder();
  225.         keystreamOnesZeroes.append(String.format("%s: ", name));
  226.         for (byte b : keystreamBytes) {
  227.             String binary = String.format("%8s", Integer.toBinaryString(b & 0xFF)).replace(' ', '0');
  228.             keystreamOnesZeroes.append(binary);
  229.         }
  230.         System.out.println(keystreamOnesZeroes);
  231.     }
  232.  
  233.    
  234.     private static byte[] xorByteArrays(byte[] array1, byte[] array2) {
  235.         int length = Math.min(array1.length, array2.length);
  236.         byte[] result = new byte[length];
  237.  
  238.         for (int i = 0; i < length; i++) {
  239.             result[i] = (byte) (array1[i] ^ array2[i]);
  240.         }
  241.  
  242.         return result;
  243.     }
  244.  
  245.    
  246.     private IntE breakCrownOTP(Real sqrt, int precision, int bitRate) {
  247.         Reals r = Reals.r(precision);
  248.         Integers z = Integers.z();
  249.         FiniteRealVectorSpace space = new FiniteRealVectorSpace(r, 3);
  250.         Real power = r.getPowerOfTwo(bitRate + 4);
  251.         List<Vector<Real>> generators = new ArrayList<>();
  252.         generators.add(new Vector<>(r.one(),  r.zero(), r.multiply(2, sqrt, power)));
  253.         generators.add(new Vector<>(r.zero(), r.one(),  r.negative(power)));
  254.         SubRealInnerProductSpace<Real, Vector<Real>> subSpace = new SubRealInnerProductSpace<>(space, generators);
  255.         RealLattice lattice  = new RealLattice(space, generators);
  256.         Vector<Real> target = new Vector<>(r.zero(), r.zero(), r.multiply(-1, power, sqrt, sqrt));
  257.         target = subSpace.project(target);
  258.         Vector<IntE> result  = lattice.asVector(space.closestLatticePoint(target, lattice));
  259.         Vector<Real> closest = lattice.fromVector(result);
  260.         IntE intPart = closest.get(1).round();
  261.         IntE diff = closest.get(2).round();
  262.         IntE seed = z.add(z.multiply(intPart, intPart), diff);
  263.  
  264.         return seed;
  265.     }
  266. }
  267.  
Add Comment
Please, Sign In to add comment