1 /** 2 * Rivest's Package Tranform 3 * 4 * Copyright: 5 * (C) 2009 Jack Lloyd 6 * (C) 2014-2015 Etienne Cimon 7 * 8 * License: 9 * Botan is released under the Simplified BSD License (see LICENSE.md) 10 */ 11 module botan.constructs.aont_package; 12 13 import botan.constants; 14 static if (BOTAN_HAS_AONT): 15 16 // todo: write unit tests 17 18 import botan.block.block_cipher; 19 import botan.rng.rng; 20 import botan.filters.filters; 21 import botan.stream.ctr; 22 import botan.utils.get_byte; 23 import botan.utils.xor_buf; 24 import botan.algo_base.symkey; 25 26 /** 27 * Rivest's Package Tranform 28 * Params: 29 * rng = the random number generator to use 30 * cipher = the block cipher to use 31 * input = the input data buffer 32 * input_len = the length of the input data in bytes 33 * output = the output data buffer (must be at least 34 * input_len + cipher.BLOCK_SIZE bytes long) 35 */ 36 void aontPackage(RandomNumberGenerator rng, 37 BlockCipher cipher, 38 const(ubyte)* input, size_t input_len, 39 ubyte* output) 40 { 41 import std.algorithm : fill; 42 const size_t BLOCK_SIZE = cipher.blockSize(); 43 44 if (!cipher.validKeylength(BLOCK_SIZE)) 45 throw new InvalidArgument("AONT::package: Invalid cipher"); 46 47 // The all-zero string which is used both as the CTR IV and as K0 48 Vector!ubyte all_zeros = Vector!ubyte(BLOCK_SIZE*2); 49 zeroise(all_zeros); 50 51 SymmetricKey package_key = SymmetricKey(rng, BLOCK_SIZE); 52 53 Pipe pipe = Pipe(new StreamCipherFilter(new CTRBE(cipher), package_key)); 54 55 pipe.processMsg(input, input_len); 56 pipe.read(output, pipe.remaining()); 57 58 // Set K0 (the all zero key) 59 cipher.setKey(SymmetricKey(all_zeros)); 60 61 SecureVector!ubyte buf = SecureVector!ubyte(BLOCK_SIZE); 62 63 const size_t blocks = (input_len + BLOCK_SIZE - 1) / BLOCK_SIZE; 64 65 ubyte* final_block = output + input_len; 66 clearMem(final_block, BLOCK_SIZE); 67 68 // XOR the hash blocks into the final block 69 foreach (size_t i; 0 .. blocks) 70 { 71 const size_t left = std.algorithm.min(BLOCK_SIZE, input_len - BLOCK_SIZE * i); 72 73 zeroise(buf); 74 copyMem(buf.ptr, output + (BLOCK_SIZE * i), left); 75 76 for (size_t j = 0; j != i.sizeof; ++j) 77 buf[BLOCK_SIZE - 1 - j] ^= get_byte((i).sizeof-1-j, i); 78 79 cipher.encrypt(buf.ptr); 80 81 xorBuf(final_block, buf.ptr, BLOCK_SIZE); 82 } 83 84 // XOR the random package key into the final block 85 xorBuf(final_block, package_key.ptr, BLOCK_SIZE); 86 } 87 88 /** 89 * Rivest's Package Tranform (Inversion) 90 * Params: 91 * cipher = the block cipher to use 92 * input = the input data buffer 93 * input_len = the length of the input data in bytes 94 * output = the output data buffer (must be at least 95 * input_len - cipher.BLOCK_SIZE bytes long) 96 */ 97 void aontUnpackage(BlockCipher cipher, 98 const(ubyte)* input, size_t input_len, 99 ubyte* output) 100 { 101 const size_t BLOCK_SIZE = cipher.blockSize(); 102 103 if (!cipher.validKeylength(BLOCK_SIZE)) 104 throw new InvalidArgument("AONT::unpackage: Invalid cipher"); 105 106 if (input_len < BLOCK_SIZE) 107 throw new InvalidArgument("AONT::unpackage: Input too short"); 108 109 // The all-zero string which is used both as the CTR IV and as K0 110 Vector!ubyte all_zeros = Vector!ubyte(BLOCK_SIZE*2); 111 all_zeros.zeroise(); 112 113 cipher.setKey(SymmetricKey(all_zeros)); 114 115 SecureVector!ubyte package_key = SecureVector!ubyte(BLOCK_SIZE); 116 SecureVector!ubyte buf = SecureVector!ubyte(BLOCK_SIZE); 117 118 // Copy the package key (masked with the block hashes) 119 copyMem(package_key.ptr, input + (input_len - BLOCK_SIZE), BLOCK_SIZE); 120 121 const size_t blocks = ((input_len - 1) / BLOCK_SIZE); 122 123 // XOR the blocks into the package key bits 124 foreach (size_t i; 0 .. blocks) 125 { 126 const size_t left = std.algorithm.min(BLOCK_SIZE, 127 input_len - BLOCK_SIZE * (i+1)); 128 129 zeroise(buf); 130 copyMem(buf.ptr, input + (BLOCK_SIZE * i), left); 131 132 for (size_t j = 0; j != (i).sizeof; ++j) 133 buf[BLOCK_SIZE - 1 - j] ^= get_byte((i).sizeof-1-j, i); 134 135 cipher.encrypt(buf.ptr); 136 137 xorBuf(package_key.ptr, buf.ptr, BLOCK_SIZE); 138 } 139 140 Pipe pipe = Pipe(new StreamCipherFilter(new CTRBE(cipher), SymmetricKey(package_key))); 141 142 pipe.processMsg(input, input_len - BLOCK_SIZE); 143 144 pipe.read(output, pipe.remaining()); 145 146 147 }