├── examples ├── decrypted │ ├── README.md │ ├── pe-pdf-ocb1.exe │ ├── pe-pdf-ocb2.pdf │ ├── pe-pdf-siv1.pdf │ ├── pe-pdf-siv2.exe │ ├── pdf-pdf-ocb1.pdf │ ├── pdf-pdf-ocb2.pdf │ ├── pdf-pdf-siv1.pdf │ └── pdf-pdf-siv2.pdf ├── input │ ├── S(24).gz.rar │ ├── PE-PDF(200-3D0-5E0).exe.pdf │ ├── README.md │ ├── gzip-rar4.gcm │ └── Yes-No(30-250-4d0).pdf.pdf ├── README.me ├── PDF-PDF.siv ├── PE-PDF.siv ├── PDF-PDF.ocb └── PE-PDF.ocb ├── README.md ├── decrypt_siv.sage ├── decrypt_ocb.sage ├── LICENSE ├── util.sage ├── mitra_tagset.sage ├── mitra_ocb.sage ├── mitra_siv.sage ├── mitra_gcm.sage ├── gcm.sage ├── gcm_siv_impl.sage ├── gcm_siv.sage └── ocb.sage /examples/decrypted/README.md: -------------------------------------------------------------------------------- 1 | Decrypted payloads from the example PoCs. 2 | -------------------------------------------------------------------------------- /examples/input/S(24).gz.rar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kste/keycommitment/HEAD/examples/input/S(24).gz.rar -------------------------------------------------------------------------------- /examples/README.me: -------------------------------------------------------------------------------- 1 | Example PoCs: 2 | - tiny (hand-made) 3 | - clean: virus-free, PII-free, copyright-free 4 | -------------------------------------------------------------------------------- /examples/decrypted/pe-pdf-ocb1.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kste/keycommitment/HEAD/examples/decrypted/pe-pdf-ocb1.exe -------------------------------------------------------------------------------- /examples/decrypted/pe-pdf-ocb2.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kste/keycommitment/HEAD/examples/decrypted/pe-pdf-ocb2.pdf -------------------------------------------------------------------------------- /examples/decrypted/pe-pdf-siv1.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kste/keycommitment/HEAD/examples/decrypted/pe-pdf-siv1.pdf -------------------------------------------------------------------------------- /examples/decrypted/pe-pdf-siv2.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kste/keycommitment/HEAD/examples/decrypted/pe-pdf-siv2.exe -------------------------------------------------------------------------------- /examples/decrypted/pdf-pdf-ocb1.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kste/keycommitment/HEAD/examples/decrypted/pdf-pdf-ocb1.pdf -------------------------------------------------------------------------------- /examples/decrypted/pdf-pdf-ocb2.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kste/keycommitment/HEAD/examples/decrypted/pdf-pdf-ocb2.pdf -------------------------------------------------------------------------------- /examples/decrypted/pdf-pdf-siv1.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kste/keycommitment/HEAD/examples/decrypted/pdf-pdf-siv1.pdf -------------------------------------------------------------------------------- /examples/decrypted/pdf-pdf-siv2.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kste/keycommitment/HEAD/examples/decrypted/pdf-pdf-siv2.pdf -------------------------------------------------------------------------------- /examples/input/PE-PDF(200-3D0-5E0).exe.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kste/keycommitment/HEAD/examples/input/PE-PDF(200-3D0-5E0).exe.pdf -------------------------------------------------------------------------------- /examples/input/README.md: -------------------------------------------------------------------------------- 1 | Input PoCs: 2 | - binary polyglots (2 valid binary payloads in the same files) 3 | - crypto-polyglots (1 payload is supposed to be ciphered when the other is in plaintext) 4 | - no overlap 5 | - block-aligned -------------------------------------------------------------------------------- /examples/input/gzip-rar4.gcm: -------------------------------------------------------------------------------- 1 | key1: 4e6f773f000000000000000000000000 2 | key2: 4c347433722121210000000000000000 3 | adata: 4d79566f69636549734d795061737321 4 | nonce: 000000000000000000000000 5 | ciphertext: 81042a95e2cd0afcbb3d74f2c7807105b1b63c5b718f83d4292f580567b4baa5e99557907298dc943233669438573a3d01031857bd51bb7577f4459a62764f3d7e7c2a42f06421c8684d98dec08d36bd88d79b58f89bbe3c338ddcebf2d0cac48fa30238a8811c20947fc8533d836bc69b27742eb2fe8a1f498a0da302727cf400000000000000000000000000000000 6 | tag: 6c4fd7abc224ac5323bca8b6a5f52f28 7 | exts: gz rar 8 | origin: S(24).gz.rar 9 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Overview 2 | 3 | This repository contains sample implementations for creating a valid ciphertext which will decrypt under two different keys for *AES-GCM*, *AES-GCM-SIV* and *AES-OCB3*. For more details on this see our paper ["How to Abuse and Fix Authenticated Encryption Without Key Commitment"](https://eprint.iacr.org/2020/1456). 4 | 5 | The implementations require [Sagemath](https://www.sagemath.org/) and the GCM and OCB implementations require [PyCryptodome](https://www.pycryptodome.org/en/latest/). 6 | 7 | The `mitra_*` versions of the script can be used to take polyglots generated with https://github.com/corkami/mitra as input. 8 | -------------------------------------------------------------------------------- /decrypt_siv.sage: -------------------------------------------------------------------------------- 1 | # AES-GCM-SIV PoC decryptor 2 | 3 | import sys 4 | import argparse 5 | 6 | load('gcm_siv.sage') 7 | 8 | if __name__=='__main__': 9 | fname = sys.argv[1] 10 | with open(fname, "rb") as f: 11 | lines = f.readlines() 12 | 13 | for line in lines: 14 | line = line.strip() 15 | l = line.split(b": ") 16 | if l[1].startswith(b"b'") and l[1][-1] == 39: 17 | l[1] = l[1][2:-1] 18 | vars()[l[0].decode("utf-8").lower()] = l[1].strip().decode("utf-8") 19 | 20 | for v in ["key1", "key2", "nonce", "ciphertext", "tag"]: 21 | vars()[v] = unhexlify(vars()[v]) 22 | 23 | m1 = AES_GCM_SIV_decrypt(ciphertext, tag, key1, nonce) 24 | m2 = AES_GCM_SIV_decrypt(ciphertext, tag, key2, nonce) 25 | 26 | with open("siv1.bin", "wb") as f: f.write(m1) 27 | with open("siv2.bin", "wb") as f: f.write(m2) 28 | -------------------------------------------------------------------------------- /decrypt_ocb.sage: -------------------------------------------------------------------------------- 1 | # AES-OCB3 PoC decryptor 2 | 3 | import sys 4 | import argparse 5 | 6 | load('ocb.sage') 7 | 8 | if __name__=='__main__': 9 | fname = sys.argv[1] 10 | with open(fname, "rb") as f: 11 | lines = f.readlines() 12 | 13 | for line in lines: 14 | line = line.strip() 15 | l = line.split(b": ") 16 | if l[1].startswith(b"b'") and l[1][-1] == 39: 17 | l[1] = l[1][2:-1] 18 | vars()[l[0].decode("utf-8").lower()] = l[1].strip().decode("utf-8") 19 | 20 | for v in ["key1", "key2", "nonce", "ciphertext", "tag"]: 21 | vars()[v] = unhexlify(vars()[v]) 22 | 23 | cipher1 = AES.new(key1, AES.MODE_OCB, nonce=nonce) 24 | cipher2 = AES.new(key2, AES.MODE_OCB, nonce=nonce) 25 | 26 | m1 = cipher1.decrypt_and_verify(ciphertext, tag) 27 | m2 = cipher2.decrypt_and_verify(ciphertext, tag) 28 | 29 | with open("ocb1.bin", "wb") as f: f.write(m1) 30 | with open("ocb2.bin", "wb") as f: f.write(m2) 31 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 kste 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /examples/input/Yes-No(30-250-4d0).pdf.pdf: -------------------------------------------------------------------------------- 1 | %PDF-1.3 2 | %µ¶ 3 | 1 0 obj 4 | <> 5 | stream 6 | 7 | %PDF-1.7 8 | %µ¶ 9 | 10 | 1 0 obj 11 | <> 12 | endobj 13 | 14 | 2 0 obj 15 | <> 16 | endobj 17 | 18 | 3 0 obj 19 | <> 20 | stream 21 | BT/F 270 Tf 30 300 Td(YES)' ET 22 | 23 | endstream 24 | endobj 25 | 26 | 4 0 obj 27 | <>>>>>/Parent 2 0 R>> 28 | endobj 29 | 30 | xref 31 | 0 5 32 | 0000000000 00001 f 33 | 0000000016 00000 n 34 | 0000000062 00000 n 35 | 0000000114 00000 n 36 | 0000000194 00000 n 37 | 38 | trailer 39 | <]>> 40 | 41 | % 42 | endstream 43 | endobj 44 | 45 | 2 0 obj 46 | 544 47 | endobj 48 | 49 | 3 0 obj 50 | <> 51 | endobj 52 | 53 | 4 0 obj 54 | <> 55 | endobj 56 | 5 0 obj 57 | <> 58 | stream 59 | BT/F 270 Tf 30 300 Td(NO!)' ET 60 | 61 | endstream 62 | endobj 63 | 64 | 6 0 obj 65 | <>>>>>/Parent 4 0 R>> 66 | endobj 67 | 68 | xref 69 | 0 7 70 | 0000000000 00001 f 71 | 0000000015 00000 n 72 | 0000000611 00000 n 73 | 0000000631 00000 n 74 | 0000000679 00000 n 75 | 0000000730 00000 n 76 | 0000000810 00000 n 77 | 78 | trailer 79 | <<49CFFE1378E7BF5BC08B50924917E1BC>]>> 80 | 81 | startxref 82 | 00000937 83 | %%EOF 84 | 85 | startxref 86 | 00000321 87 | %%EOF 88 | -------------------------------------------------------------------------------- /util.sage: -------------------------------------------------------------------------------- 1 | from Crypto.Cipher import AES 2 | from binascii import hexlify, unhexlify 3 | 4 | zero_block = unhexlify('00'*16) 5 | one_block = unhexlify('11'*16) 6 | 7 | def block_aes(block, key): 8 | """ 9 | Encrypt a 16-byte block using AES with the given key. 10 | """ 11 | assert(len(block) == 16) 12 | aes = AES.new(key, AES.MODE_CBC, iv=zero_block) 13 | return aes.encrypt(block) 14 | 15 | def block_aes_inverse(block, key): 16 | """ 17 | Decrypt a 16-byte block using AES with the given key. 18 | """ 19 | assert(len(block) == 16) 20 | aes = AES.new(key, AES.MODE_CBC, iv=zero_block) 21 | return aes.decrypt(block) 22 | 23 | def byte_array_to_field_element(block): 24 | """ 25 | Converts a 16-byte array to an element of GF(2^128). 26 | """ 27 | assert(len(block) == 16) 28 | field_element = 0 29 | for i in range(128): 30 | if (block[i // 8] >> (7 - (i % 8))) & 1 == 1: 31 | field_element += x^i 32 | return F(field_element) 33 | 34 | def field_element_to_byte_array(element): 35 | """ 36 | Converts an element of GF(2^128) to a 16-byte array. 37 | """ 38 | coeff = element.polynomial().coefficients(sparse=False) 39 | result = [0 for _ in range(16)] 40 | for i in range(len(coeff)): 41 | if coeff[i] == 1: 42 | result[i // 8] |= (1 << ((7 - i) % 8)) 43 | return bytes(result) 44 | 45 | def byte_array_to_field_element_gcm_siv(block): 46 | """ 47 | Converts a 16-byte array to an element of GF(2^128). 48 | """ 49 | assert(len(block) == 16) 50 | field_element = 0 51 | for i in range(128): 52 | if (block[i // 8] >> (i % 8)) & 1 == 1: 53 | field_element += x^i 54 | return F(field_element) 55 | 56 | def field_element_to_byte_array_gcm_siv(element): 57 | """ 58 | Converts an element of GF(2^128) to a 16-byte array. 59 | """ 60 | coeff = element.polynomial().coefficients(sparse=False) 61 | result = [0 for _ in range(16)] 62 | for i in range(len(coeff)): 63 | if coeff[i] == 1: 64 | result[i // 8] |= (1 << (i % 8)) 65 | return bytes(result) 66 | 67 | def byte_array_to_bitvector(a): 68 | result = [] 69 | for i in range(len(a)): 70 | for j in range(8): 71 | result.append(a[i] >> j & 0x1) 72 | return result 73 | 74 | def xor_block(block_a, block_b): 75 | assert(len(block_a) == len(block_b)) 76 | return bytes([a ^^ b for a, b in zip(block_a, block_b)]) 77 | -------------------------------------------------------------------------------- /mitra_tagset.sage: -------------------------------------------------------------------------------- 1 | # Sets the tag of an AES-GCM PoC output from Mitra's tool. 2 | 3 | import sys 4 | import argparse 5 | 6 | load('gcm.sage') 7 | 8 | parser = argparse.ArgumentParser(description="Sets the tag in a GCM output file from Mitra's GCM tool.") 9 | parser.add_argument('gcm_file', 10 | help="Input file generated by Mitra's GCM tool.") 11 | parser.add_argument('-t', '--tag', default='04'*16, 12 | help="Tag - default: 04*16 .") 13 | parser.add_argument('-i', '--index', default=0, 14 | help="Index of correction blocks.") 15 | parser.add_argument('-p', '--dump_plaintexts', default=False, action="store_true", 16 | help="Dump decrypted payloads.") 17 | 18 | 19 | args = parser.parse_args() 20 | 21 | fn = args.gcm_file 22 | wanted_tag = unhexlify(args.tag) 23 | index = int(args.index) 24 | 25 | with open(fn, "rb") as f: 26 | lines = f.readlines() 27 | 28 | for line in lines: 29 | line = line.strip() 30 | l = line.split(b": ") 31 | vars()[l[0].decode("utf-8")] = l[1].strip().decode("utf-8") 32 | 33 | for v in ["key1", "key2", "adata", "nonce", "ciphertext", "tag"]: 34 | vars()[v] = unhexlify(vars()[v]) 35 | 36 | assert len(ciphertext) % 16 == 0 37 | assert len(adata) % 16 == 0 38 | 39 | # we just discard the previous value 40 | tag = wanted_tag 41 | 42 | ad_str = adata 43 | num_ad_blocks = len(ad_str) // 16 44 | ad_blocks = [ad_str[i*16: i*16+16] for i in range(num_ad_blocks)] 45 | 46 | ct_str = ciphertext 47 | num_ct_blocks = len(ct_str) // 16 48 | ct_blocks = [ct_str[i*16: i*16+16] for i in range(num_ct_blocks)] 49 | 50 | # In practice, we can put these 2 blocks anywhere - even in AD - 51 | # but it's not supported here. 52 | correction_indices = [ 53 | num_ad_blocks + index, 54 | num_ad_blocks + index + 1 55 | ] 56 | 57 | ad_blocks, ct_blocks = gcm(key1, key2, nonce, tag, 58 | correction_indices, 59 | num_ct_blocks, ct_blocks, 60 | num_ad_blocks, ad_blocks) 61 | 62 | additional_data = b''.join(ad_blocks) 63 | ciphertext = b''.join(ct_blocks) 64 | 65 | print(f'Key1: {hexlify(key1)}') 66 | print(f'Key2: {hexlify(key2)}') 67 | print(f'Nonce: {hexlify(nonce)}') 68 | print(f'Adata: {hexlify(additional_data)}') 69 | print(f'Ciphertext: {hexlify(ciphertext)}') 70 | print(f'Tag: {hexlify(tag)}') 71 | 72 | if args.dump_plaintexts: 73 | cipher = AES.new(key1, AES.MODE_GCM, nonce=nonce) 74 | _ = cipher.update(additional_data) 75 | m1 = cipher.decrypt_and_verify(ciphertext, tag) 76 | 77 | cipher = AES.new(key2, AES.MODE_GCM, nonce=nonce) 78 | _ = cipher.update(additional_data) 79 | m2 = cipher.decrypt_and_verify(ciphertext, tag) 80 | with open("gcm1.bin", "wb") as f: f.write(m1) 81 | with open("gcm2.bin", "wb") as f: f.write(m2) 82 | -------------------------------------------------------------------------------- /examples/PDF-PDF.siv: -------------------------------------------------------------------------------- 1 | Key1: b'01010101010101010101010101010101' 2 | Key2: b'02020202020202020202020202020202' 3 | Nonce: b'030303030303030303030303' 4 | Ciphertext: b'923d5ed7812b036b4d38f8fc884c9cf387cc22fc65d6d2cce71e5c30c0c01516ecfb2291689650bff08d37ec086d89acc6b2488e638185d876d560a8e2b5d13346e2523cb3a276fe5f4e6caae7a0ade5d5b32623a282cd043d5ce9adc417ffb4596c41c1115127ee58eadf5e1653bf2748a0aede54580c6e66d9718b8081bf077fb746e924554352ae1a70372b3d9dddf9454c3e541c4e07bf62fdc69207e2aee3e8898748ce73362fbba4df099af635e9f611bca52ca6a84edf29d40a9e630667924ae53df33853fa5f4e36a773762375831a19102ac9fe5be1f9c276fedc5840f2ad49265730d8e7d4996ff176b3cae83b504baa69128074e8e1633ef6faecc0402eb6629dca2700bc37b2302945550d8b79283456043e2ced274b14e51308148cee55d56d7ac8fba9a201c34a5aac964c4301ab5d87b076f3b5c461ece167d42a70cc5e0fa8b290e4e623b3786dfcd3bee13a4967ba6d036dd206eff809062593ee71a2509bd48b62060b7ceb9b9413e9ef7cc3d8be9607c4580ae2f4a652d5f60515de8684d5576354be1812e90e474d5ed152ff938fd378532afa44f706f387a36486ff0b327b7e15123b6ce68296233f4a6ede4aa217af9e5c6c7124ee940167403e9a0fedd299fc528b703716c6e46a261c57daaac8405c01950d06f70a5aa00771a174ad6a51a1beaf495d7bbc1f402c12bf0860d3983d020da23fa5962cdd9fbc513122d5731adf1dc89ce3522dac509e98bbc1eaf5402bf49a18d709975ef69ddcc04846bcfac54f0a0e2f1714b88f6e463b9a7c5ff6219cf6b022ccbce0c3eba5fd97e4985eccf681b5b4c95f37cf449368d77c8bb5fbc80c9ad8cf9a9dd20210f71a9935c61715f0e75267c126a243542334cbc286c244075e600d39ea1f23c8faba9d400177891c44ce3aa56b39baa4f8df90ea4914b5cc482686895d68c689c08c166102fd31a835bbe58682107e8675217478be0d2333fa7bc23a1d132a5a5c020a87a5d0cf33efd657ca600f9621aad5cd2dc564fa2385031f82a34580896ddfe72611d7791ee42b092fbe4613526d5213b107686b4a7fc56f1f7528b373a461e534b374274b15a59479b04decf9d44708005f38b595f2bac74e415947eac1595ef5904ffecf16e74ac941a0ff0e1655bda1c8fad19315b4ef38f9d8336a73cd62812868dbdfc790b3e558189f7a6c95894f8464dd05b2533e3a77ceddac11a987d5cbb3c9256a8640d6038cf1d58d63250e871197f303318d551b496a0062c4182a39a356ee4efb6ccf4a0a495a24b7ea206d110c1d5116b0ca74d367b71bce009fc3ba63f312d37587472b3ed9c36f56f65c60c7dfb26b058c6848112ce60c134b2efc111f4e419efc33d537827a0e1507465c435794108217e32d8e6a2658901365e5f0d1f1d3ebb27fd71091eceb3333d647ccb355372049ff96b9686c25718b1701a2a35a97a6a3974e85a85b889152587e3b2c4151a49becfc29891b71917170083654dcf103a001ceccd0e676b3c7cb0d636c6c80a324d627509b6591c5741ce641b89a4d1344fcf42fa3a79f74d5be1f94532bdf367fb8004d5ae5cf892e2637b335d946c2dfd41b1867d730354c29904c89f6bf84e67f055a46f08b67d4281fc26e0b1b71baa51fc2b6f4fd926113da26ec68d1a25304315f072c35453d5a0723acd922d0d4777076584625c4815325830eaff5b890907697ad5e8e31cb1965e32a2d67a87f5677fd16607f4625fedb9bfd9851726a2e87013267ad3fe57de8aaf1634aad9308521ab35417d01cef8ef820bbc00a45baaf8916388b4712c925092aed5b226967da9b81db135cc36adb6b4872c7' 5 | Tag: b'04040404040404040404040404040404' 6 | -------------------------------------------------------------------------------- /mitra_ocb.sage: -------------------------------------------------------------------------------- 1 | # AES-OCB3 PoC generator from a Mitra-generated polyglot 2 | # Note: requires block alignment 3 | 4 | import sys 5 | import argparse 6 | 7 | load('ocb.sage') 8 | 9 | parser = argparse.ArgumentParser(description="Turn a non-overlapping, block-aligned polyglot into a dual AES-OCB3 ciphertext.") 10 | parser.add_argument('polyglot', 11 | help="input polyglot - requires special naming like 'P(10-5c).png.rar'.") 12 | parser.add_argument('-k', '--keys', nargs=2, default=['01'*16, '02'*16], 13 | help="encryption keys - default: 01* / 02*.") 14 | parser.add_argument('-n', '--nonce', default='03'*12, 15 | help="nonce - default: 03*.") 16 | parser.add_argument('-t', '--tag', default='04'*16, 17 | help="nonce - default: 04*.") 18 | parser.add_argument('-p', '--dump_plaintexts', default=False, action="store_true", 19 | help="Dump decrypted payloads.") 20 | 21 | args = parser.parse_args() 22 | 23 | fn = args.polyglot 24 | key1, key2 = args.keys 25 | key1 = unhexlify(key1) 26 | key2 = unhexlify(key2) 27 | nonce = unhexlify(args.nonce) 28 | tag = unhexlify(args.tag) 29 | 30 | cuts = fn[fn.find("(") + 1:] 31 | cuts = cuts[:cuts.find(")")] 32 | cuts = cuts.split("-") 33 | cuts = [int(i, 16)//16 for i in cuts] 34 | 35 | if len(cuts) < 1: 36 | printf("Invalid cuts parameters from filename - aborting.") 37 | sys.exit() 38 | 39 | with open(fn, "rb") as f: 40 | fdata = f.read() 41 | 42 | content_length = len(fdata) // 16 43 | m1 = [fdata[i*16: i*16+16] for i in range(content_length)] 44 | m2 = [fdata[i*16: i*16+16] for i in range(content_length)] 45 | 46 | t = 270 # 256 is ~50%, 270 is 99% 47 | m = content_length + t + 1 48 | 49 | m1 += [b'\0'*16 for _ in range(t+1)] 50 | m2 += [b'\0'*16 for _ in range(t+1)] 51 | 52 | controlled_m1 = [] 53 | controlled_m2 = [] 54 | start = 0 55 | 56 | keep = controlled_m1 57 | skip = controlled_m2 58 | for end in cuts: 59 | keep += list(range(start, end)) 60 | start = end 61 | keep, skip = skip, keep 62 | keep += list(range(start, content_length)) 63 | 64 | assert(len(controlled_m1 + controlled_m2) == content_length) 65 | 66 | ciphertext, tag = ocb(key1, key2, nonce, tag, 67 | content_length, t, m, 68 | m1, m2, 69 | controlled_m1, controlled_m2) 70 | 71 | print(f'Key1: {hexlify(key1)}') 72 | print(f'Key2: {hexlify(key2)}') 73 | print(f'Nonce: {hexlify(nonce)}') 74 | print(f'Ciphertext: {hexlify(ciphertext)}') 75 | print(f'Tag: {hexlify(tag)}') 76 | 77 | if args.dump_plaintexts: 78 | cipher1 = AES.new(key1, AES.MODE_OCB, nonce=nonce) 79 | cipher2 = AES.new(key2, AES.MODE_OCB, nonce=nonce) 80 | m1 = cipher1.decrypt_and_verify(ciphertext, tag) 81 | m2 = cipher2.decrypt_and_verify(ciphertext, tag) 82 | with open("ocb1.bin", "wb") as f: f.write(m1) 83 | with open("ocb2.bin", "wb") as f: f.write(m2) 84 | -------------------------------------------------------------------------------- /mitra_siv.sage: -------------------------------------------------------------------------------- 1 | # AES-GCM-SIV PoC generator from a Mitra-generated polyglot 2 | # Note: requires block alignment 3 | 4 | import sys 5 | import argparse 6 | 7 | load('gcm_siv.sage') 8 | 9 | parser = argparse.ArgumentParser(description="Turn a non-overlapping, block-aligned polyglot into a dual AES-GCM-SIV ciphertext.") 10 | parser.add_argument('polyglot', 11 | help="input polyglot - requires special naming like 'P(10-5c).png.rar'.") 12 | parser.add_argument('-k', '--keys', nargs=2, default=['01'*16, '02'*16], 13 | help="encryption keys - default: 01* / 02*.") 14 | parser.add_argument('-n', '--nonce', default='03'*12, 15 | help="nonce - default: 03*.") 16 | parser.add_argument('-t', '--tag', default='04'*16, 17 | help="nonce - default: 04*.") 18 | parser.add_argument('-p', '--dump_plaintexts', default=False, action="store_true", 19 | help="Dump decrypted payloads.") 20 | 21 | args = parser.parse_args() 22 | 23 | fn = args.polyglot 24 | key1, key2 = args.keys 25 | key1 = unhexlify(key1) 26 | key2 = unhexlify(key2) 27 | nonce = unhexlify(args.nonce) 28 | tag = unhexlify(args.tag) 29 | 30 | cuts = fn[fn.find("(") + 1:] 31 | cuts = cuts[:cuts.find(")")] 32 | cuts = cuts.split("-") 33 | cuts = [int(i, 16)//16 for i in cuts] 34 | 35 | if len(cuts) < 1: 36 | printf("Invalid cuts parameters from filename - aborting.") 37 | sys.exit() 38 | 39 | with open(fn, "rb") as f: 40 | fdata = f.read() 41 | 42 | key1_auth, key1_enc = derive_keys(key1, nonce) 43 | key2_auth, key2_enc = derive_keys(key2, nonce) 44 | 45 | while(1): 46 | T1_tmp = recover_POLYVAL(key1_enc, tag, nonce + unhexlify('00'*4)) 47 | T2_tmp = recover_POLYVAL(key2_enc, tag, nonce + unhexlify('00'*4)) 48 | if T1_tmp and T2_tmp: 49 | break 50 | tag = inc(tag) 51 | 52 | T1 = byte_array_to_field_element_gcm_siv(T1_tmp) 53 | T2 = byte_array_to_field_element_gcm_siv(T2_tmp) 54 | 55 | num_blocks = len(fdata) // 16 56 | m1 = [fdata[i*16: i*16+16] for i in range(num_blocks)] 57 | m2 = [fdata[i*16: i*16+16] for i in range(num_blocks)] 58 | 59 | t = 2 60 | num_blocks += t 61 | m1 += [b'\0'*16 for _ in range(t)] 62 | m2 += [b'\0'*16 for _ in range(t)] 63 | M1 = [byte_array_to_field_element_gcm_siv(block) for block in m1] 64 | M2 = [byte_array_to_field_element_gcm_siv(block) for block in m2] 65 | 66 | 67 | controlled_m1 = [] 68 | controlled_m2 = [] 69 | start = 0 70 | keep = controlled_m1 71 | skip = controlled_m2 72 | for end in cuts: 73 | keep += list(range(start, end)) 74 | start = end 75 | keep, skip = skip, keep 76 | keep += list(range(start, num_blocks)) 77 | skip += keep[-2:] 78 | 79 | assert(len(controlled_m1 + controlled_m2) == num_blocks + t) 80 | 81 | ciphertext, tag = siv(key1, key2, nonce, tag, num_blocks, 82 | m1, m2, controlled_m1, controlled_m2) 83 | 84 | print(f'Key1: {hexlify(key1)}') 85 | print(f'Key2: {hexlify(key2)}') 86 | print(f'Nonce: {hexlify(nonce)}') 87 | print(f'Ciphertext: {hexlify(ciphertext)}') 88 | print(f'Tag: {hexlify(tag)}') 89 | 90 | if args.dump_plaintexts: 91 | m1 = AES_GCM_SIV_decrypt(ciphertext, tag, key1, nonce) 92 | m2 = AES_GCM_SIV_decrypt(ciphertext, tag, key2, nonce) 93 | with open("siv1.bin", "wb") as f: f.write(m1) 94 | with open("siv2.bin", "wb") as f: f.write(m2) 95 | -------------------------------------------------------------------------------- /examples/PE-PDF.siv: -------------------------------------------------------------------------------- 1 | Key1: b'01010101010101010101010101010101' 2 | Key2: b'02020202020202020202020202020202' 3 | Nonce: b'030303030303030303030303' 4 | Ciphertext: b'fa371a91ac1a2d58471d3a494afa96c2a7fc029307bcd8f0db311055aea7617eccc902a148c46e81fafe439e6d0ce4a6ec15c8932eee0aabe77a6150564f11133600bafb10885fcee1d75bc3c6f8edaaa5a5bd5abd48a72066a915624f01f80d6579bba75890067a737a0dc109205a79768426e083b1ae7b95ba4a474ed91a35471106c6016622a361e6dfd126b1170d0ed687e80a3c0cf02c6a56b47eb0094d5a015ebe959ad2ebe5da450d1656742d6ce4d06b4cbb516d85665c300b66b18ceaf04350295119e247b75a53cfabc859a0e29ded5cd44740abae68891157a4552a2c2ac1f17d8801c34e1a8fd402f3cbb8e635ff6081a539fe340b314e23535d79c6026c7901c23087ee6bc9722ca95e2019a095d973c90d0772848b6382c59bf88f385dad499a3ec4b1702cca485ff67483673437d7800399c196f06b5e20850a9c335636a30b1172fae6ffaa8aa54a4022bce27e93eb2c12cf6763832a5ae9292124252de5a967d4007ac09239eb31256d19cfce5536e4b6b82dc920394f74ff7e0cf1c47cab714072fa6d54f52238e8e9e4db606bb6384884b46bad421e5624087c7742ab1d2735df9c83dcc61f97cf19b2fa5ee9d15fac7c55f6365114536723fb192aff458650859ea4f932d94f7fedaf728bf57daf305496923d095c17abe05e540988f8c00623b8a04e12aba8c32816c0c86f64d51d8cc448ecb1508e8f4ca89cd4265c50ed179c289d3fa4ac5134ad34c3cae5fa908b573db9ae3fcd72c816d2c0f1965122e8aa900a6b04496400da8911091fc12b04ad0dd3cfc637cca4b79fdc91ffa1c1e927bc93aee5d53e4569a7522347272eb6e535d85cf330e603bff2116c7a5410742b29cd6e7f276c9395f168764f13489d613579d47f87c75f6149e5a9dade16ac696435fb6ac41a03e5c33d985a931f33346b6fd57b8d90f48a335474992d000c23b2be57b994541a6370dc337c17952d2e310564c023780b047df60fe5ce10c8f603225757de0edc7588e80e004551f381b563f96efae7e588a557c1d9893adec7019d160ff077f1a5af4e0b87a1bf3b233fad3af4f60abcbc4bb9cd4015ea513b96cc8a727990ca43fa53718f400008a44c71d4284ae3d9f2a8975895ffecb659d26dad21018d7fe76bb77b38689c947f560d552821b6754226c6c1fb9cf071e693c2d3bb5c831a79f31deb9104f461aaf7ed1ba1aa3558ed76c3efea5cdb4b95742f40f921c344addd8e136c2ca93ce14249e4ff40d091e818864a0b325ffda22b1aa19276012206e95ac17cd61badffbd57e6e6d3f818100d5227565c80decf64e782bd468924bdc9a91ec7cefd10db642bbbcd4f03f21a5843ae9d3f273eb087d97c5370becc03e507b25a60350d766c7367a420b221d21cae5a1668a023458bd0dbc1e3db824fe720a7dfda1303e677fc837559685bdfe689a749cd51fd20bb4697facd966ca2f0b5e26b74e9070422643b250ef6c7b8ed13916c76df1f2f4f754489b956fb458a8d82ba23aafdeb63436666f4d145bf4b35e96fadf95b4f8f3bddba7aac06939b5b8394bf44f5ec302d0770e3a45b8045967ad589be1867078473e9057cffe0298232e009027d6ad10fb9ce8bbada4b3c1900b6ce56e71f278759495df4e8e62ffaf4c1cd154327e317db2de0e0103a6c6139263f36493b7557d4ab47b6de02382f1c266e98d53c73d54be0b3c2e2f918e3cb5e8e31cb1965e32a2d67268fcf9ca522a34f17444009e926964ef8ed33df5611418ddb0c9cc567a12fc9e9f5cf4f21ef9cc3faea24d030f91a1e719f04d4e22c4317630d5d1d2237a987e972fd68b463dcb8d47406b8887c47b095fd35b688628e754f3e37a992fbb2ef4eb9b917d29530798b912b848cbfffa198071896257b7eb878ce739b1c20a5dfd8b03e6f920b1570184e4cf4ecb6e2f5416a3fe30a806805f457beaef9bfa9233a141713f713fcf459cbebcacc5344e3aace64aa6e3514581ad53eea1d097155fad0f9d2a5e7125ddec3ed06620eb0bf2d7216741764a01cf1a2d8ea058748657b650ac99b8296e61030a22829e225704096e83484ec44aa7b26c921c5f0b3d5285e38bb898a9bae28acdc22d3742a97eb6d8086419d901999bf9b83be644661d77126ae568ec021fafb07093d09ebb9490de5b725e9d92b81d916da6109a31d6cddcf8d514d01e9b4c0f412af2f102a' 5 | Tag: b'04040404040404040404040404040404' 6 | -------------------------------------------------------------------------------- /mitra_gcm.sage: -------------------------------------------------------------------------------- 1 | # AES-GCM PoC generator from a Mitra-generated polyglot. 2 | # Bruteforces the nonce if a near-polyglot is used. 3 | 4 | import sys 5 | import argparse 6 | import binascii 7 | from Crypto.Util.number import long_to_bytes,bytes_to_long 8 | 9 | load('gcm.sage') 10 | 11 | 12 | def mix(d1, d2, cuts): 13 | """mixing data with exclusive parts of each data""" 14 | assert len(d1) == len(d2) 15 | d = b"" 16 | start = 0 17 | keep = d1 18 | skip = d2 19 | for end in cuts: 20 | d += keep[start:end] 21 | start = end 22 | keep, skip = skip, keep 23 | d += keep[start:] 24 | return d 25 | 26 | 27 | parser = argparse.ArgumentParser(description="Turn a non-overlapping, block-aligned polyglot into a dual AES-GCM ciphertext.") 28 | parser.add_argument('polyglot', 29 | help="input polyglot - requires special naming like 'P(10-5c).png.rar'.") 30 | parser.add_argument('-k', '--keys', nargs=2, default=['01'*16, '02'*16], 31 | help="Encryption keys - default: 01*16 / 02*16 .") 32 | parser.add_argument('-n', '--nonce', default='03'*12, 33 | help="Nonce - default: 03*12 .") 34 | parser.add_argument('-a', '--additional_data', default='aa'*32, 35 | help="Additional Data - default: AA*32 .") 36 | parser.add_argument('-t', '--tag', default='04'*16, 37 | help="Tag - default: 04*16 .") 38 | parser.add_argument('-i', '--index', default=0, 39 | help="Index of correction blocks.") 40 | parser.add_argument('-p', '--dump_plaintexts', default=False, action="store_true", 41 | help="Dump decrypted payloads.") 42 | 43 | args = parser.parse_args() 44 | 45 | fn = args.polyglot 46 | key1, key2 = args.keys 47 | key1 = unhexlify(key1) 48 | key2 = unhexlify(key2) 49 | nonce = unhexlify(args.nonce) 50 | additional_data = unhexlify(args.additional_data) 51 | tag = unhexlify(args.tag) 52 | index = int(args.index) 53 | 54 | # GCM cuts are at byte boundary 55 | cuts = fn[fn.find("(") + 1:] 56 | cuts = cuts[:cuts.find(")")] 57 | cuts = cuts.split("-") 58 | cuts = [int(i, 16) for i in cuts] 59 | 60 | if len(cuts) < 1: 61 | printf("Invalid cuts parameters from filename - aborting.") 62 | sys.exit() 63 | 64 | with open(fn, "rb") as f: 65 | fdata = f.read() 66 | 67 | def xor(_a1, _a2): 68 | assert len(_a1) == len(_a2) 69 | return bytes([(_a1[i] ^^ _a2[i]) for i in range(len(_a1))]) 70 | 71 | def bruteNonce(fn): 72 | hdr1 = fn[fn.find("{")+1:] 73 | hdr1 = hdr1[:hdr1.find("}")] 74 | hdr1 = binascii.unhexlify(hdr1) 75 | 76 | hdr2 = fdata[:len(hdr1)] 77 | hdr_xor = xor(hdr1,hdr2) 78 | hdr_xor_l = len(hdr_xor) 79 | aes1 = AES.new(key1, AES.MODE_ECB) 80 | aes2 = AES.new(key2, AES.MODE_ECB) 81 | 82 | i = 0 83 | for i in range(2**(8*hdr_xor_l)): 84 | block1 = aes1.encrypt(long_to_bytes((i << 32) + 2, 16)) 85 | block2 = aes2.encrypt(long_to_bytes((i << 32) + 2, 16)) 86 | 87 | if xor(block1[:hdr_xor_l], block2[:hdr_xor_l]) == hdr_xor: 88 | return i 89 | return None 90 | 91 | 92 | if fn.startswith("O") and \ 93 | "{" in fn and \ 94 | "}" in fn: 95 | nonce = bruteNonce(fn) 96 | print("Overlap file found - bruteforced nonce: %s" % nonce) 97 | nonce = unhexlify(b"%024x" % nonce) 98 | 99 | cipher = AES.new(key1, AES.MODE_GCM, nonce=nonce) 100 | _ = cipher.update(additional_data) 101 | c1, _ = cipher.encrypt_and_digest(fdata) 102 | 103 | cipher = AES.new(key2, AES.MODE_GCM, nonce=nonce) 104 | _ = cipher.update(additional_data) 105 | c2, _ = cipher.encrypt_and_digest(fdata) 106 | 107 | ciphertext = mix(c1, c2, cuts) 108 | 109 | num_ad_blocks = len(additional_data) // 16 110 | ad_blocks = [additional_data[i*16: i*16+16] for i in range(num_ad_blocks)] 111 | 112 | # if index is null, then we append 2 blocks and use them for correction 113 | if index == 0: 114 | if len(ciphertext) % 16 > 0: 115 | ciphertext += b"\0" * (16 - len(ciphertext) % 16) 116 | index = len(ciphertext) // 16 117 | ciphertext += b"\0" * 32 118 | 119 | # In practice, we can put these 2 blocks anywhere - even in AD - 120 | # but it's not supported in this script. 121 | correction_indices = [ 122 | num_ad_blocks + index, 123 | num_ad_blocks + index + 1 124 | ] 125 | 126 | num_ct_blocks = len(ciphertext) // 16 127 | ct_blocks = [ciphertext[i*16: i*16+16] for i in range(num_ct_blocks)] 128 | 129 | ad_blocks, ct_blocks = gcm(key1, key2, nonce, tag, 130 | correction_indices, 131 | num_ct_blocks, ct_blocks, 132 | num_ad_blocks, ad_blocks) 133 | 134 | additional_data = b''.join(ad_blocks) 135 | ciphertext = b''.join(ct_blocks) 136 | 137 | print(f'Key1: {hexlify(key1)}') 138 | print(f'Key2: {hexlify(key2)}') 139 | print(f'Nonce: {hexlify(nonce)}') 140 | print(f'AdditionalData: {hexlify(additional_data)}') 141 | print(f'Ciphertext: {hexlify(ciphertext)}') 142 | print(f'Tag: {hexlify(tag)}') 143 | 144 | if args.dump_plaintexts: 145 | cipher = AES.new(key1, AES.MODE_GCM, nonce=nonce) 146 | _ = cipher.update(additional_data) 147 | m1 = cipher.decrypt_and_verify(ciphertext, tag) 148 | 149 | cipher = AES.new(key2, AES.MODE_GCM, nonce=nonce) 150 | _ = cipher.update(additional_data) 151 | m2 = cipher.decrypt_and_verify(ciphertext, tag) 152 | with open("gcm1.bin", "wb") as f: f.write(m1) 153 | with open("gcm2.bin", "wb") as f: f.write(m2) 154 | -------------------------------------------------------------------------------- /gcm.sage: -------------------------------------------------------------------------------- 1 | from Crypto.Cipher import AES 2 | 3 | load('util.sage') 4 | 5 | # The finite field used in GCM 6 | F2. = GF(2)[]; 7 | p = x^128 + x^7 + x^2 + x + 1; 8 | F = GF(2^128, 'x', modulus=p) 9 | 10 | # A complete example for GCM which allows to construct a single ciphertext + tag 11 | # which can be decrypted under two different keys. 12 | # 13 | 14 | def gcm(key1, key2, nonce, tag, 15 | correction_indices, 16 | num_ct_blocks, ct_blocks, 17 | num_ad_blocks, ad_blocks): 18 | # Derive some of the constants we need to compute the tag. 19 | H1 = byte_array_to_field_element(block_aes(zero_block, key1)) 20 | H2 = byte_array_to_field_element(block_aes(zero_block, key2)) 21 | tag_mask1 = byte_array_to_field_element(block_aes(nonce + unhexlify('00000001'), key1)) 22 | tag_mask2 = byte_array_to_field_element(block_aes(nonce + unhexlify('00000001'), key2)) 23 | len_block = byte_array_to_field_element(unhexlify(hex(num_ad_blocks*128)[2:].zfill(16)) + unhexlify(hex(num_ct_blocks*128)[2:].zfill(16))) 24 | 25 | # Convert additional data, ciphertext blocks and target tag value to field elements. 26 | A = [byte_array_to_field_element(block) for block in ad_blocks] 27 | C = [byte_array_to_field_element(block) for block in ct_blocks] 28 | TAG_VALUE = byte_array_to_field_element(tag) 29 | 30 | # Concatenate additonal data and ciphertext blocks for equations below. 31 | AC = A + C 32 | num_blocks = num_ad_blocks + num_ct_blocks 33 | 34 | # Construct two linear equations: 35 | # 1) Ensures that the tag values are equal for both keys. 36 | # 2) Forces a specific tag value. 37 | sum_h1 = sum([H1^(num_blocks + 1 - i) * AC[i] for i in range(num_blocks) if i not in correction_indices]) 38 | sum_h2 = sum([H2^(num_blocks + 1 - i) * AC[i] for i in range(num_blocks) if i not in correction_indices]) 39 | 40 | b1 = sum_h1 + sum_h2 + len_block*H1 + tag_mask1 + len_block*H2 + tag_mask2 41 | b2 = TAG_VALUE + tag_mask1 + H1*len_block + sum_h1 42 | 43 | a00 = H1^(num_blocks - correction_indices[0] + 1) + H2^(num_blocks - correction_indices[0] + 1) 44 | a01 = H1^(num_blocks - correction_indices[1] + 1) + H2^(num_blocks - correction_indices[1] + 1) 45 | a10 = H1^(num_blocks - correction_indices[0] + 1) 46 | a11 = H1^(num_blocks - correction_indices[1] + 1) 47 | 48 | # Solve system of linear equations 49 | A = Matrix(F, [[a00, a01], [a10, a11]]) 50 | b = vector(F, [b1, b2]) 51 | AC[correction_indices[0]], AC[correction_indices[1]] = A.solve_right(b) 52 | 53 | # Place the solution in the original additional data and/or ciphertext blocks. 54 | for cor_idx in correction_indices: 55 | if cor_idx < num_ad_blocks: 56 | ad_blocks[cor_idx] = field_element_to_byte_array(AC[cor_idx]) 57 | else: 58 | ct_blocks[cor_idx - num_ad_blocks] = field_element_to_byte_array(AC[cor_idx]) 59 | 60 | # Recompute tag and check that they are equal. 61 | tag1 = sum([H1^(num_blocks + 1 - i) * AC[i] for i in range(num_blocks)]) + H1*len_block + tag_mask1 62 | tag2 = sum([H2^(num_blocks + 1 - i) * AC[i] for i in range(num_blocks)]) + H2*len_block + tag_mask2 63 | assert(tag1 == tag2) 64 | 65 | return ad_blocks, ct_blocks 66 | 67 | 68 | if __name__ == "__main__" and __file__ == "gcm.sage.py": 69 | # The following variables can be of any value: 70 | key1 = unhexlify('01'*16) 71 | key2 = unhexlify('02'*16) 72 | nonce = unhexlify('03'*12) 73 | tag = unhexlify('04'*16) 74 | 75 | # Ciphertext is given as 16-byte blocks. 76 | num_ct_blocks = 6 77 | ct_blocks = [b'\xcc'*16 for _ in range(num_ct_blocks)] 78 | 79 | # Additional data is given as 16-byte blocks. 80 | num_ad_blocks = 2 81 | ad_blocks = [b'\xaa'*16 for _ in range(num_ad_blocks)] 82 | 83 | # We need to control 2 blocks in order to be able to solve system of linear equations, which 84 | # can be either in the additional data or the ciphertext part. Note that if we don't fix the 85 | # tag it would also be possible to use a single block. 86 | # 87 | # Indices for additional data are 0...num_ad_blocks - 1 88 | # Indices for ciphertext are num_ad_blocks..num_ad_blocks+num_ct_blocks - 1 89 | correction_indices = [0, 4] 90 | assert(len(correction_indices) == 2) 91 | 92 | ad_blocks, ct_blocks = gcm(key1, key2, nonce, tag, 93 | correction_indices, 94 | num_ct_blocks, ct_blocks, 95 | num_ad_blocks, ad_blocks) 96 | 97 | # Check that we can decrypt this with a third-party GCM implementation with both keys: 98 | try: 99 | additional_data = b''.join(ad_blocks) 100 | ciphertext = b''.join(ct_blocks) 101 | cipher = AES.new(key1, AES.MODE_GCM, nonce=nonce) 102 | _ = cipher.update(additional_data) 103 | plaintext = cipher.decrypt_and_verify(ciphertext, tag) 104 | 105 | cipher = AES.new(key2, AES.MODE_GCM, nonce=nonce) 106 | _ = cipher.update(additional_data) 107 | plaintext = cipher.decrypt_and_verify(ciphertext, tag) 108 | 109 | # Everything looks good 110 | print(f'Key1: {hexlify(key1)}') 111 | print(f'Key2: {hexlify(key2)}') 112 | print(f'Nonce: {hexlify(nonce)}') 113 | print(f'AdditionalData: {hexlify(additional_data)}') 114 | print(f'Ciphertext: {hexlify(ciphertext)}') 115 | print(f'Tag: {hexlify(tag)}') 116 | except: 117 | print('ERROR: Could not decrypt ciphertext.') 118 | -------------------------------------------------------------------------------- /gcm_siv_impl.sage: -------------------------------------------------------------------------------- 1 | """ 2 | This modules contains a basic AES-GCM-SIV implementation and some 3 | GCM-SIV specific utility functions, which will be used 4 | for the attack. 5 | """ 6 | from Crypto.Cipher import AES 7 | import struct 8 | 9 | load('util.sage') 10 | 11 | F2. = GF(2)[]; 12 | p = x^128 + x^127 + x^126 + x^121 + 1; 13 | F = GF(2^128, 'x', modulus=p) 14 | 15 | def AES_GCM_SIV_encrypt(plaintext, key, nonce): 16 | """ 17 | Encrypt with AES-GCM-SIV as described in https://datatracker.ietf.org/doc/rfc8452 18 | """ 19 | 20 | message_auth_key, message_enc_key = derive_keys(key, nonce) 21 | plaintext_length = len(plaintext) 22 | 23 | len_block = unhexlify('00'*8) + struct.pack(b' = GF(2)[]; 6 | p = x^128 + x^7 + x^2 + x + 1; 7 | F = GF(2^128, 'x', modulus=p) 8 | 9 | def double(block): 10 | """ 11 | Takes a 16-byte block and applies double. 12 | """ 13 | tmp = [0 for _ in range(16)] 14 | for i in range(15): 15 | tmp[i] = ((block[i] << 1) & 0xff) | (block[i+1] >> 7) 16 | tmp[15] = ((block[15] << 1) & 0xff) ^^ ((block[0] >> 7) * 135) 17 | return b''.join([bytes([i]) for i in tmp]) 18 | 19 | def compute_L_i(L_dollar, i): 20 | L = double(L_dollar) 21 | while(i&1 == 0): 22 | L = double(L) 23 | i = i >> 1 24 | return L 25 | 26 | def derive_initial_L_and_offset(key, nonce): 27 | L_star = block_aes(zero_block, key) 28 | L_dollar = double(L_star) 29 | L_i = double(L_dollar) 30 | 31 | # Nonce derivation 32 | tmp_nonce = [0 for _ in range(16)] 33 | for i in range(len(nonce)): 34 | tmp_nonce[16 - len(nonce) + i] = nonce[i] 35 | tmp_nonce[16 - len(nonce) - 1] = 0x01 36 | bottom = tmp_nonce[15] & 0x3f 37 | tmp_nonce[15] &= 0xC0 38 | ktop = block_aes(b''.join([bytes([i]) for i in tmp_nonce]), key) 39 | tmp_bytes = b'' 40 | for i in range(8): 41 | tmp_bytes += bytes([ktop[i] ^^ ktop[i + 1]]) 42 | stretch = ktop + tmp_bytes 43 | byteshift = bottom//8 44 | bitshift = bottom%8 45 | 46 | offset = [0 for _ in range(16)] 47 | for i in range(16): 48 | if bitshift != 0: 49 | offset[i] = ((stretch[i + byteshift] << bitshift) & 0xff) | (stretch[i + byteshift + 1] >> (8 - bitshift)) 50 | else: 51 | offset[i] = stretch[i + byteshift] 52 | 53 | offset = b''.join([bytes([i]) for i in offset]) 54 | return L_dollar, offset 55 | 56 | def block_encrypt_decrypt(block, key1, key2, offset1, offset2): 57 | """ 58 | This encrypts a block of 16-bytes first with key1 and using offset1, then 59 | will decrypt it with key2 using offset2. 60 | """ 61 | tmp = xor_block(offset1, block_aes(xor_block(block, offset1), key1)) 62 | return xor_block(offset2, block_aes_inverse(xor_block(tmp, offset2), key2)) 63 | 64 | def ocb(key1, key2, nonce, tag, 65 | content_length, t, m, 66 | m1, m2, 67 | controlled_m1, controlled_m2): 68 | 69 | # Generate all the masks 70 | L1_dollar, offset1 = derive_initial_L_and_offset(key1, nonce) 71 | offsets1 = [offset1] 72 | for i in range(m): 73 | L1_i = compute_L_i(L1_dollar, i + 1) 74 | offsets1.append(xor_block(offsets1[i], L1_i)) 75 | offsets1 = offsets1[1:] 76 | 77 | L2_dollar, offset2 = derive_initial_L_and_offset(key2, nonce) 78 | offsets2 = [offset2] 79 | for i in range(m): 80 | L2_i = compute_L_i(L2_dollar, i + 1) 81 | offsets2.append(xor_block(offsets2[i], L2_i)) 82 | offsets2 = offsets2[1:] 83 | 84 | # Compute the checksum we need to have in order to get the correct tag with 85 | # each key. 86 | T1 = xor_block(xor_block(block_aes_inverse(tag, key1), L1_dollar), offsets1[-1]) 87 | T2 = xor_block(xor_block(block_aes_inverse(tag, key2), L2_dollar), offsets2[-1]) 88 | 89 | 90 | # We have to fix the uncontrolled blocks in m1/m2 so they encrypt to the same ciphertext as m1/m2. 91 | for idx in controlled_m1: 92 | m2[idx] = block_encrypt_decrypt(m1[idx], key1, key2, offsets1[idx], offsets2[idx]) 93 | for idx in controlled_m2: 94 | m1[idx] = block_encrypt_decrypt(m2[idx], key2, key1, offsets2[idx], offsets1[idx]) 95 | 96 | # Modify m1 in order to get the correct tag value. This guarantees that as long 97 | # as the checksum is 0 for m1[m - t] ... m1[m] the tag will be correct. 98 | m1[m - t - 1] = reduce(lambda x, y: xor_block(x, y), m1[:(m - t - 1)] + [T1]) 99 | m2[m - t - 1] = block_encrypt_decrypt(m1[m - t - 1], key1, key2, 100 | offsets1[m - t - 1], 101 | offsets2[m - t - 1]) 102 | 103 | # Update target checksum for the free blocks after fixing the first blocks of m2. 104 | T2 = reduce(lambda x, y: xor_block(x, y), m2[:(m - t)] + [T2]) 105 | 106 | # Generate the gamma values 107 | m2_blocks_zero = [] 108 | m2_blocks_one = [] 109 | gamma_0 = [] 110 | for i in range(t//2): 111 | # Encrypt and decrypt a pair of two all zero message blocks and XOR them. 112 | # Note that any pair of messages which are equal would work here. The only 113 | # condition here as that if we sum up all the blocks the checksum stays 114 | # zero. 115 | tmp1 = block_encrypt_decrypt(zero_block, key1, key2, 116 | offsets1[m - t + 2*i], 117 | offsets2[m - t + 2*i]) 118 | 119 | tmp2 = block_encrypt_decrypt(zero_block, key1, key2, 120 | offsets1[m - t + 2*(i + 1) - 1], 121 | offsets2[m - t + 2*(i + 1) - 1]) 122 | 123 | m2_blocks_zero.append(tmp1) 124 | m2_blocks_zero.append(tmp2) 125 | gamma_0.append(byte_array_to_bitvector(xor_block(tmp1, tmp2))) 126 | 127 | gamma_1 = [] 128 | for i in range(t//2): 129 | # Encrypt and decrypt a pair of two all one message blocks and XOR them. 130 | tmp1 = block_encrypt_decrypt(one_block, key1, key2, 131 | offsets1[m - t + 2*i], 132 | offsets2[m - t + 2*i]) 133 | 134 | tmp2 = block_encrypt_decrypt(one_block, key1, key2, 135 | offsets1[m - t + 2*(i + 1) - 1], 136 | offsets2[m - t + 2*(i + 1) - 1]) 137 | 138 | m2_blocks_one.append(tmp1) 139 | m2_blocks_one.append(tmp2) 140 | gamma_1.append(byte_array_to_bitvector(xor_block(tmp1, tmp2))) 141 | 142 | # Construct the system of linear equations to find the correct 143 | # combination of pairs which we have to use in m2 to get the 144 | # correct tag. 145 | equations = [] 146 | 147 | # Equations to ensure that summing up will gives us the correct 148 | # checksum in the end. 149 | for bit_pos in range(128): 150 | tmp = [] 151 | for i in range(t // 2): 152 | tmp.append(gamma_0[i][bit_pos]) 153 | tmp.append(gamma_1[i][bit_pos]) 154 | equations.append(tmp) 155 | 156 | # Equations to ensure that either the zero pair or one pair is used. 157 | for i in range(t // 2): 158 | tmp = [0] * 2*i + [1, 1] + [0] * (t - 2*i - 2) 159 | equations.append(tmp) 160 | 161 | A = matrix(GF(2), equations) 162 | # Right-hand side of the equation is just the target checksum, and 163 | # all 1 for ensuring that at each index only one pair is valid. 164 | b = vector(GF(2), byte_array_to_bitvector(T2) + [1]*(t//2)) 165 | 166 | try: 167 | solution = A.solve_right(b) 168 | except ValueError: 169 | print('Could not find a solution for the system of linear equations. ' 170 | 'You can try increasing the value t or a different combination of keys/nonce.') 171 | exit(1) 172 | 173 | # Set the final message depending on the solution to the system of 174 | # linear equations. 175 | for i in range(t): 176 | if solution[2 * (i // 2)] == 1: 177 | m1[m - t + i] = zero_block 178 | m2[m - t + i] = m2_blocks_zero[i] 179 | else: 180 | m1[m - t + i] = one_block 181 | m2[m - t + i] = m2_blocks_one[i] 182 | # Check if this message will give us the correct tag. 183 | message1 = b"".join([block for block in m1]) 184 | cipher = AES.new(key1, AES.MODE_OCB, nonce=nonce) 185 | ct1, tag1 = cipher.encrypt_and_digest(message1) 186 | 187 | message2 = b"".join([block for block in m2]) 188 | cipher = AES.new(key2, AES.MODE_OCB, nonce=nonce) 189 | ct2, tag2 = cipher.encrypt_and_digest(message2) 190 | 191 | # Check if everything is correct. 192 | assert(ct1 == ct2) 193 | assert(tag1 == tag2) 194 | 195 | return ct1, tag1 196 | 197 | 198 | if __name__ == "__main__" and __file__ == "ocb.sage.py": 199 | # Construct ciphertext which works for two keys 200 | key1 = unhexlify('01'*16) 201 | key2 = unhexlify('02'*16) 202 | nonce = unhexlify('03'*12) 203 | tag = unhexlify('04'*16) 204 | 205 | # Fix the message length so we can compute all the mask values needed in advance. 206 | # For the attack we need to control t + 1 message blocks. For the sample attack 207 | # we assume that the blocks containing the actual message content are in the beginning 208 | # while the blocks used for forcing a correct tag are in the end. There is no restriction 209 | # for this, but it allows to keep the implementation here simpler. 210 | # 211 | # t should be ~256 to have a good probability of finding a solution. 212 | content_length = 6 213 | t = 256 214 | m = content_length + t + 1 215 | 216 | # Set the value of the two messages. Note that most of these values will be overwritten. 217 | m1 = [b'\xaa'*16 for _ in range(m)] 218 | m2 = [b'\xbb'*16 for _ in range(m)] 219 | 220 | # In order to get the correct ciphertext, we will always need to control 221 | # either the block in m1 or m2. The following indices determine which 222 | # blocks of plaintext are preserved either in m1 or m2. 223 | controlled_m1 = [0, 1, 2] 224 | controlled_m2 = [3, 4, 5] 225 | assert(len(controlled_m1 + controlled_m2) == content_length) 226 | 227 | ciphertext, tag = ocb(key1, key2, nonce, tag, 228 | content_length, t, m, 229 | m1, m2, 230 | controlled_m1, controlled_m2) 231 | 232 | print(f'Key1: {hexlify(key1)}') 233 | print(f'Key2: {hexlify(key2)}') 234 | print(f'Nonce: {hexlify(nonce)}') 235 | print(f'Ciphertext: {hexlify(ciphertext[:32])}...') 236 | print(f'Tag: {hexlify(tag)}') 237 | -------------------------------------------------------------------------------- /examples/PDF-PDF.ocb: -------------------------------------------------------------------------------- 1 | Key1: b'01010101010101010101010101010101' 2 | Key2: b'02020202020202020202020202020202' 3 | Nonce: b'030303030303030303030303' 4 | Ciphertext: b'2c92143d3c1ecf1c76c24c3a1b4e041fa2bd14a0469ebad98e607753b28c4643561176534218951cabc0c382c899ffa20fb6a4c7334992040a3b6249170bf6f273ae588ed8b7dd3f668cad1f874dd85f09c2d4d853ae676ca4ee60bac3387b19d93df8ee618d4fd64e17af1d2f3e38d667901e8ce26e885d60d7e40cab90a35f9d6e4a42aae1d27d3ff8d7cea9aad4c7908554963cd85365e1aa8a39b84052752db92fc112ff76ee79772cb895c010768790b20a0042c318ed7100cc76a216c4cac41511f50fea7fce564ed18cbc0475a3dda90ab389ca36feda779eeefd88d184f2eaab78be0b4e1fc85130ce259e60503c2fbce9b03451cf13c4fc3283e5abbbb9680590ae7fd45228b59c3d0d392d48b5898698fddb6894ab3fa38b0828daee812dc5479390ba701e653e8d575652999e5d6a36595c71cba4ab701f02ad1a63b968efe2bc17c9075a3bb936c072896a0cd4ff6e1987c375d1d11e3ed059d2be8a4af528671f5c638b1e9d2476262b03ed19518099823d85e6c8b75bd13d3b68d252da1a4c203a60a8d20eba032d4672e46e85d1d170110f146c9904d85e94847faacae08f2d3fd26aa13cd0349ed2050fd48546611399383bbc958323ff26852b46779237b783999d0c3121c6ea4e76dafa3ab9c788bc1c913215e6b4a3e7708a7df9bff7f7683dff47455a8560c2b35aad47b0f19cb3b7c068e593f576e98de18afed7d3b4b337ce2f47ee32e7c4b632feba088cca7b5efeee5d126ea7552cb5edc983a45c92265f15df956734a877728426354f17210b6d4a0a0a12b85ea98ba360111b0bcb4e295388b8bbff2812db9dc7d4803e641c4aee98ec55c8bc349a0bd06af54ff244a11cdb07d2b4853012f1aace39175473c2060c758507be6c330b9e323f52b48ef06e63d18defab7fe6affa0e964e2b76453fabbe6945baf90f98b39814859cb719a158b17a271ec3e872a32f040fc0afbeb3645f7f896515643d1d953421fb8b87b7a5c473ed3d1cad91464299988c7f4f50fad776af860443798645d03772636c43df7c32791ed6cb4bfe5fe24060233a3b4918e0ae4619845d8d3656fb60e391d7dea142574501f3a9bb9edec131157bd451848f6f300af107265f7d0ddf3859c0e1df0195c461a0ccb76b286ef9b9e3845e39520ea0ef23f35fd0729d4f7e8bac13e61906f7506d27c002dc659bdf62bea7f2567895542b6b05d381d505e96f9d07790ddf91904a2ecb8f4483533f221195f185f8fb696708965cb2d03bd94846cb837fee315553749dd9c4047ec5f4377ebcf05a4af2b2f41c008c7b2540d38946058bf68b900dde2397d27c8be082899a656f6a949df09a7715e7aeafef6bff7a202f25068eae3c5bdac7bf844aa55c210c1946c4ce1ee6c4677616f9a4546b6f8ab5002aa8f6105557e29763bf0a8998171ab60bb5e54204c1eff314e8a1368dee535d46302977f654b6546d69e6f398f26561f4feefded8f584a6f6c48849240f77345bdf75f174c5d576e849cb2ddf2e31e989c86c6f617f940268deef0d06ff22b0af0fbce9268b17fb5f0a6ff20bf696f19d661a4d1a35dab9d3ef3c4b055dc9e206c83c345d086f2bf7c1ebe3db7bc154aa6089b43d27ad74844b2755557e28b787d1dd3fe3255be86fa4781592b15b744dd2f4ca48cd9ab8f910c189f0f767498c6cbf4ce8e9fb58dbecb12321409453cb18b1eda1e3724243cf472f169324bacb4d410909fa7705edb40db6b2a10a5a6e1f9e6813fc54e77bde8a218daa72733386df4c537d978c3b5e80368ffd2737fea92ba38b288f436f51d0e4455d9b7c1dcf1e872f04967cb62134498cf65a1eec68c3e1d0a314587d52bd6d894aaaf49e4e19dbd504c563cf675de2ce88c88e630569fee43532111ee9f1e9a24f0b50db670a824e8a452fc21cbfde2270e0a5aea68f2393f069002a6138240493a9bf134cd27b3619891e24b00fe4746f079c6e36df06fada217bca807fd923d3c8dfabdb3a5c454b34e4cb22e3aca647099cfe475f13613c2ea84f3bb5f7af441a447549986443eaf99cb6327469926e4c6a696be0a46dcc8237033c5d9e1c3366bddca75fb5addf62d82c3787e4d5133068ffca061004d4877246bb5ff7b1d30882d478e8ce5c5b58b1e43cbe6c95b9a7b8cac93446b40879771caf601a7b35b7400577ea69e03a366cfa85e30d3678efc4c8775d3e89718e969e656c50b568698bcaa574b060914d9ef5322a537a70925654811d8d74912cd1da74ce8585898efdd62d49454c824fda05d6e8b62fcbf0a1189cdca0172459f99e7b2e4719f55581a4b405e1aa24eb7cd0686b1e287a6bf16304f77b292d9f259148025e3939f7e5ee564a775adec23ca64e9b8235ce9b32ada8fb94422fc2843737fd0d6244675a50ad809f45a147fc57ae9aac55b976a14681621fd0eb2c6ca60a24114707d19eb9b285350786de165929bc7a72df5f1b8f382a7a8668a1f62f1b820feba3a083e879cf47480315d5ad75af8e3ae43b34088b5d00d891917d426e28755cea0f52d803bc46e52eba59c733c2d2c4e95bf0f534ad9abfc06a14b2af7c6643313d97c7a918fcfc623ff1b1dc2dfb8f11eeed9b0259a4baaeccac978883482060a9d89ee56f9b2963919b4dc9cceab0c980c0696d03f95ee2d67f10eae086d8ced96804c38e17adf4b9cf2a9cc319c60d560c3d9e2fb326e73fbee861ac6b8f6228a686e2ff80af6be5b26da77bfade4972f91f9ca8ede48a49c125b92faad9bf65e722d277230e9459161d932ad09ead24c2b50e4dc5e9bc8823f5cb70ee9c48bbe3fd9c7ed597ac46f2d96a7231a9adcee461a87f1795a8356f5d2af33859933dcc2c7a262dec25b56aed8d053d2f64a0263769705a9c00bef3721cbb7df21bea5fdd98c569c44ff751855f52e9ff553881e0afe2af822da83c3f65836e459335ca833b2c95a5cfd060b9dede5aaf5ff86136fdf061f4b098c4bb6e8827a3326bbf90032705613798629865ce20aa3f911ebd30fcca47da52bd3bc82785475c60ea25a885439d21436c33dc1d7b7803fd361adfc6c812ca9aa7bd74f5788e304c9fe7b548251083d8d9d90fb0bbf9022c6f3ce371e1dc898b37548bd29fbcf8f1354e0bbd99868aa3f0442065ef52e22db1ac763849f19d5fc29c606057d9c9b9f800e5bc4dc5756486251a17b885c5d79a7b044be507123164ef4999718e9f49e7256c420972df20d54280d4e20b51f2b749cf272ecc9c5fed48601b33c0b4ee958c68567d8e8d90a646bbf17bb2e5252c28b81af302793f108e304b0b8f50208271f3d4e41f086951d8b6a473846c0f6b6926640e2698ccde895b8a74550f1db7034e43fc4b07b1595785d8ee0becbd493f88816bf905910b78356f1e7baf577022d1b6929e95856975575a5367ebd848e504a08a9b9c23efd9e0634364678a6a6882a18bc9ec49757648aea3cd60357ca23d01721b28c22db266129f0c8a68ee8d14a3dddf1cffa8a1cafce7bad6bb80395ee186adaba4ffdc7ff8f1741a3510b133e435c1fc660213119beec5492a1d29b385da6f3e0f62066bc0198eb7809e9efd9ba37ca63c022779b19dfdd4708cdb15d11994a7613d9bb2754d26569523facb06f553632bb4a3cc71ddcaf2853ab52e7f3fb3b982caeec33d6c147a94103c7f6d905df15f2091d0813bb1262ced7d3fce79a9e5357fe5cc04edc03603494eb2e75e0a944d28e1c8f0eb10c3fcc16b90c62963b5e23cba286fa54969f6a4b5c704f7df2e8ea9c4a318f810bdf5e61cc88b741dc3a176418c90fa5f874fd5ad73e5d3f97262a5af21ff1b62b8af8cdead617e63145b0147ee152530353070c9f3c3751691dd085f81755a498376c84468c6fbf052555598b7112ab8dcb94af19128b872d71d409724e3e7bce27bce67056baec2997c36a057ca745121106e4f6ee5d2475da14e4c2cca24ff4239e41cf57e6b061a6e7f04488336308ae6a61eb4abb99707f896a9f26ab3b109bb18748b9059d15de6c3bb95e5d6ab74e52dd23eacced6b413c298923326ac2d3a1892b45144b27b0fdaaa3fafe30edb5bbbaca2168a5b5b9a473cce0b93af0dece56804ab54635b4f1c73cd18b426c9082c324c02df605e7b9c53583a02693154a6f254a28ccb93272d627ae2f05d04498cd2261be37ed3f12f3b8ea0c34518ebd5c77c9ef49583d09e071c32ab8b514c638d86ea63f8c94fad4d2a3abb8e557fe4a0755de0b8cd59bb441460278058d9a2d9c03e64125f83fcb5e1bc177c60dd0b24607bcff1e9c9381abb6b49cc77d266a63aba465e7bad5a5c37394d03ffc0daa7d6efb5d16cc057c3c531f9501dc09004b9fdc64eb8ee6557e3fb3dd63d62c29e610545082c74bb0aa71ad8bc6e144af837411bcd3716e659f323d2b4fb12ddd01d1a503b3b2869a1f8fb287d664803c076d643af6e0c83dbf1b286eaca5df7ca6e0c58db1d6f6e07461f2f01c8346d00e51ab046ef7fe8ea3e09540c4e05484183e2b3714337a5ef5e3408b2430b17c3b36625d50e336bb0924d1cb68d1f9c7d7a940f54c3a6d3d27efe4152f3e359276174df2596a16524c0326aa79ff0b0697c2c38d1cced83ffcb244c1d3644beb88358941a1f6826af0e7be29a76acc2f67b330973ccb102704ba40d96bd2c8b804e2b0beebea5c029e8e655e7f3425729eba6d9d20d4fb5489beef3cfa95c7e6ba0779864ce48dfb087242b5b0d72f03d22e7083fe66ef219071afddda0f7a2d3bd6ec69f435dfd5ef8426ee84337aea20a3cf4037295695a698debeaedc69247742b985a9cd0c6e09f791f30a276c4cd4c0119e7b4b2f3023fb3b6268fd1c0788b73108f8d4018f18d6ee60076f9acabb87095d26a1873f11935b9514fc4ce358ca71d81e8807292044290cabc2f31758a79006e83813e050d0495195a55012b9616d484eccaf4d3ea934037d240df4dae710e2d51ff121fe6737c549f8fbd8b0096607a36e502944e0787510184f3247ad0633eb45cf47712deb82934ae36d92ca55a1d307aa8ef47aedd8fdc8322326c9af8d1dc648d9ffb4a283f3142c9ccd464013b68ebe25ed7be1f5f384ce56d7d01d720ef19edc44f6833a26ef6a68a779176ec239591d04e92170a0a5e88f1db965898cbda9dac3ec55d0d0d81b3b6cbdbd75b609047c580ea3d666a8125d5e0435c8d1de733b4c10632592ebad2870aeea6cee2445a77531371c6054eedcc07122314edd15a274cb531923069e9c465a45bb5bf61c07fd9baee951de5cee704212d3da65d186805180b5e428f402b28df4a2ab6f6de201909e507080bcd8345dbf322f2765ac841f9b68f99fa9dd5760da61e0a585f3f42815e8ade9239dde921d17e3fd463e9fedb89c00096e4c4f0c510e6c9607a5d72f618b4eb1cf53258f881c29e9f7e27246877f6cd23f4226db74b3171faca7e0d9e3a22c1b3f4b7e8893cebfe581a4b2974277c301db1e788489e685f7f1caf102098b863161e394f3d3a817503bfdbfc0d5406b6a2509a192205f026573b0f79fd55e3c2bd3dcc73078dc9042b9c2918f9f9abd79d7f75d089094541ba0a93acd0afb46675287d8303f6f337016ec0729dd721e23a5cce573ee13b840db8bef2e7e80cea60b59a8b1985f06488f5211a0826d5fc7a2d97e59412eb1f27b5b326ffdfbfb9c9de28fda5380c23b16d8d7019f0c5a4251112fb472f1692fbad6c74c6880b339e4ccd949985594645501a44ab8cecf04d7f2f442c6ccc928c99874aa882a5e99b4d7e30b1595878a409e9475c869d06d20f27b1967e6be1769f4e7020f4c2935a4ba5d456b4db1a976a415a56c833caa409e1511b5407cc372e5ec46e7a25a55a4c9c33fa44a0aeee3b0dc2b91f2947e513ea9ce055e8e5b8b05de79796c69ca4a068b79cefac696d00c868ae616cfd0f1d9c25ae51d18b9fc19b76a6b80ecb3b26ca660925fa4beb5c4ac0871c795c1a38a5765991aefe1fbcebdbb06a10d4e9730551b3ad1fd19f3559c982bfc9fbf9f5902984c1f203e029356eea723d408d7c9dd2c4f35adeeb307ffc17b3b2fa6d7b133ff31d80b1725b8e3d86e013c03d429779324e9ddb42d9190688953c9d1e5d4e168a8faddb0678f470333fdd53af60596b27e974a669ea35731df3e3c4c3f409323e30168974257771509f335188fb25de66ad4abdbdb6e6d4b0a02a4691969c2fc94d6a84dd54db0f81d673265c2818f063884abd1fd2a9dbf038fde330ec060c185da63e348c460f7691481ab7f9bc4eaf9c0fabb8e6d493ee8efa3fe475e7d275208a83344bc66d493ecabf8e8d036c42c1ad57f87ffdfb301acc0cda8ba42488ef1b64abd31887be0402a79365a918c13ab72efcf0c7a5036fe7be28f747a5cac8de75b455f601242705447499bfcb352b0d8f256bd972defd75ffeafcda24fd183881aa659aae134a1977d532fb8954eb8d785b941e126c519e64e13f65a342cd62f146c9dbf79e9a8e88c21c59631bb5544197bf3550cc6fbbb1d4982678f0752223df97284bc0c59f51c2dfcd38dcc46bc4c34d8dd8a65b3b197eb9567323c68155b7a4d85b99fe9aef251f6e1b763efcfc20621dc1e0394594ade179f0bbb6217e458ea97956d5613b59626d46a25ae4596f0ec7e7579004523ba2f052e6ef77a5310d5f1a5a4c9a5179eabd894dc45dd41322102b45a7035d4f7bdceca83efab10a37af49689119aacc8b6a6f6b15333c94d21a250adf2fd59f1a002a363a4fabfdb488311aa535b3ee1d636efc4a3c0b62d9b0d283c503848e024e2aa1c02d0e1669432405803a405675778a3e742d051af0752e55bfc6e7e4590ded9ad22910da2b93430125254a215c1cd05609d4a8816bf8a95162cac090e29c559e5f9b91739ab940d5c984fb317f5ebbc150a40573bdca6fec2c92633db6ea244589c6bde6001e11473956429d76ad02dfd984fbdb236767e0d3fcd593a5d83e292f397e016ed0d3573996b54a40f5369ebb428a7747dbc6a9c441c15c2b489c760123759833257e631a4f8cb82ed0dea3177e3859a193f5b9f4e341b998981bc8499fe74efc6634d290f2617bf40441558f943f289d01c5f13eb9f1086166b0ced2b8e0a2b9b68a6d65b870cb084c3b6e2247e05f1a7d17ae2cb14d051fbd274a1e8ad905996d81510467030e3a1235bfe58ec37bdef61376d2ab94ea2fafc08f98af0cc40376e3c24b9b514ee41e1fb16aebe24c7a0ed719a98725b422ed7fcfdfc29001a2c40992c11417b972b140a4c21819eb1751ba0bb9af9c9dbefde1ac0bfcd2464d2ac3bc9a987c16ece0dd9fb2e3efc5e6f3e8917b19aafe986a0fec4e9748108d5413ae269995f63269b0df6ddb9114999f58c2d298de0c3f18f690f8a2e13e34103a1c0064dae2b6e6443367918b9afd45cfa80a4aecc5855c42c667f45194fb226708094d4d4325b8369b765c56a90d1ed9cbdbe262a317bac77bcb4d2f277cbd1f44ae42408acc5dac645a862812e18fc697dedf679ab0bbdc7358516d56f52634d3163a5a5d7b1c05cb2a49246d69d4e1725d31d03cfb16625032d3a70d096757505e2bd54fb2882e301a39729760ddf796e4ad90c068fe9449bc8b0129c5fb742099083a114ac098b2c58c2448373a844018f937ad04edc75325fc416c06fb88027307ac34f974a705fc570c424d1525d2f5dc39a4f3e0768b72f7e6d449a9357e5c3acebec0e61f802f93d6899b4c82e70696a7f63a9f18bcae1b2da8abf8e0b6972d2e65d13e42d6f7fa2a4647ba56ecba9041cca2aa81da13de45de1d18feb07fe5da0b677915fd5c1dcfac68df14d53701b23aae8bd93aa93198554e872c60df0bb1b5ba3fc2e5f3a1d0d8cf7f060ac21a404a5d5a53da120fb26da21cd42f0ac187a7bcc69e3b3964b26a2baecc5fe84e378aa760' 5 | Tag: b'04040404040404040404040404040404' 6 | -------------------------------------------------------------------------------- /examples/PE-PDF.ocb: -------------------------------------------------------------------------------- 1 | Key1: b'01010101010101010101010101010101' 2 | Key2: b'02020202020202020202020202020202' 3 | Nonce: b'030303030303030303030303' 4 | Ciphertext: b'854ab0ee6047c9da5e3c49e6f244ae9eda276584b786c43f6b03d7a8e2a78af46eaa1af89a2c06b854143a281dbd9b8af87987530dc380033e6a4ad3c5bd260d696e54b440003640286c6c9f71d64d360c98e714ba3a1cdbb6841ad2db5bf507166ba4500dc38075cb5c1b16ff4cd5e44568ae379f20de1ba4561bbde4fc8ff4c38f79777bddc4cd3084aeb9ffb13da3c75fc12d3545ba5a6139eb97cb5f945c5181edc47443201a233f320aa972e89729b25e020eca54f2cdba4c381bf6e29589ef9d8c6ff67d324f5e54557e7ae4e7e54f4bcd6866b93190325e35e669c8e2ca10ed590f3305ca4dd3bffadf37b4692f4be3d75bab986b881f5387d882b14ed11c816e94c04463f1fdd88a9165fbf3f04b4b0a29a38590ac04740d6768632e74dc3e394a4baced3ba50f74a2f57bb4b58ed8c809a1ec5de5f7760591ec66be03372f08315dd42ec5fcd50fc33cce95cef7391f2d96b5ada22b07b0912b2849620d44f45f5034aa9e0e96b8b2ac29912ac2c3dddc0ad3755971f53257a20f9fd5125c6b20c0c4e8df1c9ca89990b813b6ee5785527713114f11619be548b4d5264e6189be5a6c8726b0f282414ca6c1e6ffa373d1b2b8fc34ee28713f599f41261a5adb894f9fbcd5084cfe678a26b5dbb821e4f4c7a0bf50e093584b4b13e2fd35093a26ed1135e7d0299e116e22182ca9bcdc15c646d99cbd533d2d8adbc56f6829f857866499beced17b2ab187eae082061d22b0fbfb35376f3b6f3512571a527900b2a9c2510231e306343858e7d146f0a8a564985a1823b8346227148cf21d58affaa3b66ac4ae14f0ad02a8f1e7a2df2b8a9689d86d2022bd57ec51c6b213049e54f1f16e949dd29e4a465bfb11a0948d8afee598f16b26ff02d221e2ac10d0ca663df21b74694e09eaae24e1cf79be2176578c1c5823802c6545b9c01e2e1ce1d2bfe6abf31c6c00e2a36a7f6fedb77e3b9208d506bfc79a170645e620d247ac54b9aa97e3a1002169c9af34f16d0994a48e426234a93061cc33964cec31341120f9d04af40deeb08736fc1a9e8e6d359ea0fc74ad963e844c88c5ed4bf29bbc5689722211c7d84a6ec3f59fe969d1c769819926f7dc2f78276b1379e933e4dc074d3bbebc651e7f4e80c9503e3545d45ad974052b8223f2cf5452e073499be617bea3a38fae930f749684817dfbfa761fb86a7dcbadf1e6f375caa6d3b3ed56d0962665bc1bac62c056c8434de81106f7660e1b6f1b50f9c8a4d45b84726491e1dfb473c656626efc7d3a1bd9a75f9a8d3d233a76ec963b079e6950d65c599aa0bbbb8737cfe1c9b29679b204eee02437b2f6c453baed9f8fbaaf827942ff0b5007a35cf3402273b0695d0d9112d0cef4fe8a58262759ce52300419d5bc666da474e3d55d977b38f87b1d6d0ca8d77848aeada7aac1e0028668e07b23d27cff7d2aedb216e43652bb18df6a0b772542ddc9a84873ce1a961e0fbf89cb2157fe2a30371ebfd44174bd9ffe1826801eb316cc79a3d3d6f0c6c250d112efe313f08e4ae924d24681c577c5c9bf70477a0f107a48c02203aafa0714e67afd4fd7b1330d9e6f94455bef74936e921d3abc953041983beaf73636e294e52ec17fdabf22e87fbc5da682bf272c456defa6adbedce2776ecd3ff2ff1b58c98264582b8b560f97c5663d133dc94c81b0721ef5bb8316a1d5883c2e25ac6c79cf598548298185383953983f69c61b4e984c964c85e28c9626ef903153c36bf144d08e344cc96662adf8e6a7791b07d8fdfed660c05bde6808fd2f73fbdd0099f2972ae6423618e67e339130e54fe1d269a45a80184157273bb5cb2ae8b8b6afbe92cdf41c12e30de260fbea242c3b26734e19dbd504c563cf675de2ce88c88e630569fee43532111ee9f1e9a24f0b50db670a824e8a452fc21cbfde2270e0a5aea68f2393f069002a6138240493a9bf13407f88ad15c8431f2a07c9ceb989b82f3679fc0ccc0c5c6d0ceca33098850cbefbfb7ecd19eafb010cff95d22fcab20c2986edae62e6e23a0d79c32e0803166945bf7bab2d5aa7cc581ab56a63cf2c5ea3917338568cdd39a34de0b2c5f24905a75fb5addf62d82c3787e4d5133068ff7681ba219a156e6e13f5e276807b11c3aa5d92c603f4dda5f77f10cb812475b433f5d723af13bc81edcb2e09d31180ee77ea69e03a366cfa85e30d3678efc4c8775d3e89718e969e656c50b568698bcae19f87f3d6d4ef7a5d5ec42f5ed7ed93a467d1c82f0fdf081f7702b4bf99f263b4c7819598461f6190a58f708bfedd524f5a17a105a66060dbc3cc0f4004d9a5b405e1aa24eb7cd0686b1e287a6bf16304f77b292d9f259148025e3939f7e5ee564a775adec23ca64e9b8235ce9b32ada8fb94422fc2843737fd0d6244675a50ad809f45a147fc57ae9aac55b976a14681621fd0eb2c6ca60a24114707d19eb9b285350786de165929bc7a72df5f1b8f382a7a8668a1f62f1b820feba3a083e879cf47480315d5ad75af8e3ae43b34088b5d00d891917d426e28755cea0f52d8792d9672f6f6feb713f1fd2def8277079ff8ec9f9f2c2cd2ec0e88fc92d32023a918fcfc623ff1b1dc2dfb8f11eeed9b0259a4baaeccac978883482060a9d89ee56f9b2963919b4dc9cceab0c980c0696d03f95ee2d67f10eae086d8ced968046af94ae617109217060b46eb62d5ab56790ec37ca5df97978af9c54bae71f01f96f728047b17cff06640ea4d6aa72937349e9edc1855612a93dfe4b661bcb52eebc39e5a0c724d030292e83073992af2f3919bb40c0df5933f43e52d667eb3259c7ed597ac46f2d96a7231a9adcee461a87f1795a8356f5d2af33859933dcc2c918a5c58bfd022a97a011a92422f0b41f89887b8218231bb69339ac4b75fbbfa98c569c44ff751855f52e9ff553881e0afe2af822da83c3f65836e459335ca833b2c95a5cfd060b9dede5aaf5ff86136fdf061f4b098c4bb6e8827a3326bbf90032705613798629865ce20aa3f911ebd30fcca47da52bd3bc82785475c60ea25715d2d37a2684a621dd3a76888c84a00a6911729db8cc8f9c811757ecd160134b548251083d8d9d90fb0bbf9022c6f3ce371e1dc898b37548bd29fbcf8f1354e0bbd99868aa3f0442065ef52e22db1ac763849f19d5fc29c606057d9c9b9f800e5bc4dc5756486251a17b885c5d79a7b044be507123164ef4999718e9f49e7258e137dde51af858204b7046ba9c150082353749879638eaf7230283df12797943d31b44f2794483534b4f5ec159e89ff3ffac5396e7f3bdaf13f6c1f180a167df3d4e41f086951d8b6a473846c0f6b6926640e2698ccde895b8a74550f1db7034e43fc4b07b1595785d8ee0becbd493f88816bf905910b78356f1e7baf577022d1b6929e95856975575a5367ebd848e504a08a9b9c23efd9e0634364678a6a6882a18bc9ec49757648aea3cd60357ca23d01721b28c22db266129f0c8a68ee8d14a3dddf1cffa8a1cafce7bad6bb80395ee186adaba4ffdc7ff8f1741a3510b10860f3f3b642019a5644fddc2372bf9935773fe7c19cc4afb25c628507336e71300c7bafd24e69042a2aa8e372240b5cf49eaaaf278c424ec7181a8219c0ae6ecb06f553632bb4a3cc71ddcaf2853ab52e7f3fb3b982caeec33d6c147a94103c7f6d905df15f2091d0813bb1262ced7d3fce79a9e5357fe5cc04edc03603494e40a534fa5b4b90ecd6b47075501d9b8d036f4a50c811ad06bb91e9966fb0fc7b6b3730765f36dd4831b6fb41c6c3dd256f3b5cb507d3e4cffd630dae9314661173e5d3f97262a5af21ff1b62b8af8cdead617e63145b0147ee152530353070c9f3c3751691dd085f81755a498376c84468c6fbf052555598b7112ab8dcb94af1f95f9d316b3daaf6b37236da5a77be81bebf7c400d96865d220a733e44f604ef6ee5d2475da14e4c2cca24ff4239e41cf57e6b061a6e7f04488336308ae6a61eb4abb99707f896a9f26ab3b109bb18748b9059d15de6c3bb95e5d6ab74e52dd23eacced6b413c298923326ac2d3a1892b45144b27b0fdaaa3fafe30edb5bbbaca2168a5b5b9a473cce0b93af0dece56804ab54635b4f1c73cd18b426c9082c326b4f95a5aa7da85dca0b17772ad7bcd4d4ea014de3f9428e1ac6b9ed2dccddfd261be37ed3f12f3b8ea0c34518ebd5c77c9ef49583d09e071c32ab8b514c638d86ea63f8c94fad4d2a3abb8e557fe4a0755de0b8cd59bb441460278058d9a2d9620241bcb8e71803409f2025cec42388834b2870c4d4b4fb943e43344ff97ee4641fb911699ca587e4a4fa87d30f81a68ab6d121aa965f7637c0853a8ede4823b9fdc64eb8ee6557e3fb3dd63d62c29e610545082c74bb0aa71ad8bc6e144af880b59144467c98895d0e3dd4879f835e10c48dde06d205127ba86f544967c7c1c84cb6383079ad1de435c7ba10e7084d1eb5b92bd82d88c772cbe123e9f1e265ea0332ee5187fd85d1d4aefdc2c9f545477954845d8e416182931483ea3ebe26d0a79860f344ccdb0b4e62b2a0920d34306614438e2f693d9831832a421f46126174df2596a16524c0326aa79ff0b0697c2c38d1cced83ffcb244c1d3644beb88358941a1f6826af0e7be29a76acc2f67b330973ccb102704ba40d96bd2c8b807d511005efd8f78098f76f2743edaf8181f6a4e422999b6157b1f3f10e86b1c5a0779864ce48dfb087242b5b0d72f03d22e7083fe66ef219071afddda0f7a2d3fbdcc4bf0bc0d1e9dbd7060baa7811607975eec5ed9142f1a6f63360d1a75680742b985a9cd0c6e09f791f30a276c4cd4c0119e7b4b2f3023fb3b6268fd1c0789b06a6212c6157927f4ba4c9d429ca8b051326755feb4633bac5852aff481da061ee697d838f014f35a96e3aa46f1e88060b975dfc0b3849aec53e927118a8ac638198ebd12af7ce2a4bf46c829de24647d29ca02c7ea49a798029f59917b66f0096607a36e502944e0787510184f3247ad0633eb45cf47712deb82934ae36d957708b415955b3520935b4ce76adcb7ec0315da6284b1ac0544f6a2a27804a8fd464013b68ebe25ed7be1f5f384ce56d7d01d720ef19edc44f6833a26ef6a68a779176ec239591d04e92170a0a5e88f1db965898cbda9dac3ec55d0d0d81b3b6cbdbd75b609047c580ea3d666a8125d5e0435c8d1de733b4c10632592ebad2870aeea6cee2445a77531371c6054eedcc07122314edd15a274cb531923069e9c465a45bb5bf61c07fd9baee951de5cee704212d3da65d186805180b5e428f402b28df4a2ab6f6de201909e507080bcd8345dbf322f2765ac841f9b68f99fa9dd51fe04f401c7a54ebdde1349d1420caedbaae358131033adb4edf5b34e524f43c4023abca1f6f5dc95abf8775e7aab6b0ce212b85520768f0d30d0e2ac6f4a37a1d05721e73487cb48a2a156eec229210bd4fd834815e01c5bdaaba79181fa7317f6124deb117a72bda2673e0ff1d7a994425ee2bf71f817fa164c93916dbfe49192205f026573b0f79fd55e3c2bd3dcc73078dc9042b9c2918f9f9abd79d7f7599d54b70dcdfd102a892cc0e4b38cf29e16a501186a47eeb090c5d9173a60994430131eae1e4fcbfa5e96acee5c73420b4ae873da18ce3665e0776579abc1758e59412eb1f27b5b326ffdfbfb9c9de28fda5380c23b16d8d7019f0c5a4251112fb472f1692fbad6c74c6880b339e4ccd949985594645501a44ab8cecf04d7f2fc097ae6e1321bd817935727048d7026f879f8c6b6a127568c78f4254160243301d1c641b5acfd6268710ccd90779135806ae1af0b3ca5495ff7853190c71a6ce511b5407cc372e5ec46e7a25a55a4c9c33fa44a0aeee3b0dc2b91f2947e513ea9c0c192522a44d907b58506135dd247e08eb8c2faa47b90076a21d1c621795a025ae51d18b9fc19b76a6b80ecb3b26ca660925fa4beb5c4ac0871c795c1a38a5765991aefe1fbcebdbb06a10d4e9730551b3ad1fd19f3559c982bfc9fbf9f5908baf91660715bce7dbd9c130742a042bdb88bb78108d9bea79b5e9cd5b2be3713ff31d80b1725b8e3d86e013c03d429779324e9ddb42d9190688953c9d1e5d4efd5286ec6db557d6750ef8e440631af7b974ba50067fb9b218caf1425410b3a94bc29a06ee87dec29b1299fda2a6b13d6d8a473d0f89daa906cf6819c74fca1fc6275d801aa12f6351bd05216d0c1b797e36ac98b04de34f13d9a43763dcbc1ebf22a09f0301125487bf665bed332b01519e21e73eb970accbd3dd70b7d6e17ed275208a83344bc66d493ecabf8e8d036c42c1ad57f87ffdfb301acc0cda8ba42333209a44d73c902fc4e2273162fa9f3cbf49c43f46c58a95490de6db8783c433b3b957d9f2f51c87502156073bdf217b90594ffea8e85466e31c1a0fbe110724fd183881aa659aae134a1977d532fb8954eb8d785b941e126c519e64e13f65a342cd62f146c9dbf79e9a8e88c21c59631bb5544197bf3550cc6fbbb1d4982678f0752223df97284bc0c59f51c2dfcd38dcc46bc4c34d8dd8a65b3b197eb9567323c68155b7a4d85b99fe9aef251f6e1b763efcfc20621dc1e0394594ade179f0bbb6217e458ea97956d5613b59626d46a25ae4596f0ec7e7579004523ba2f0af0f83868e1e538316dacb79c86eb9f193ace9454b608c4e9513ed417e7c045c14b7382395727c51625a8a09506b2d13710534d28d599538676cdd959f17500d2a363a4fabfdb488311aa535b3ee1d636efc4a3c0b62d9b0d283c503848e024e19bb71a9d617af992bb4120e5986e1ba0064dd3dd10910fe8a1b4dd4eb45b683ed9ad22910da2b93430125254a215c1cd05609d4a8816bf8a95162cac090e29cde337cb9af85df347302f4c111a22861dabd7812bba23e08dd2049304faeced3244589c6bde6001e11473956429d76ad02dfd984fbdb236767e0d3fcd593a5d8322d63e1d9cd72f1f7e4568704840fceb24308fc3af2d2565bf4ed653735a7089c760123759833257e631a4f8cb82ed0dea3177e3859a193f5b9f4e341b998983df6046ea5a8c37a1d51c3a797e29caea0c3f2c2a7773e9776a9fb7084da5784b35e94f611de602014561ecaadb8ef39feabdd2227d5afda4329b126e15c456d274a1e8ad905996d81510467030e3a1235bfe58ec37bdef61376d2ab94ea2fafc08f98af0cc40376e3c24b9b514ee41e1fb16aebe24c7a0ed719a98725b422ed901d0a874cc80b35f8d8c5ec7f60c0201e82fc779ed47e840b9c1af8880bc622e1ac0bfcd2464d2ac3bc9a987c16ece0dd9fb2e3efc5e6f3e8917b19aafe986a42ea7c2a5ecb513c1afd89d0068b34392134f9f736780d4cd2bd2e09309314d9f690f8a2e13e34103a1c0064dae2b6e6443367918b9afd45cfa80a4aecc5855c0f668ddc3b02b5bbf763c94e6726bcd5ff7ce7fddf252bb00cf66b1dcb689d6d7f66ed5618a3ed0ca00b22e147bf2b2b7d9df7c014fa1002eace9c5756c74cc2dc7358516d56f52634d3163a5a5d7b1c05cb2a49246d69d4e1725d31d03cfb16154f7a75ecf41038adf72c96358f02a452bcbf9af4a1a7d388e6b24d62b40ab59449bc8b0129c5fb742099083a114ac098b2c58c2448373a844018f937ad04ed7bf56f4a1faa849617c99a96b672667f65a9c6b473f3d640e5f5f22de08b08c5768b72f7e6d449a9357e5c3acebec0e61f802f93d6899b4c82e70696a7f63a9f18bcae1b2da8abf8e0b6972d2e65d13e42d6f7fa2a4647ba56ecba9041cca2aadfcf1ba460f8a5c663e47e81178b649027c93c313f72ce2dc2b9da3aa4a85e00ed5c4ecc23de5660787cb6ba6c017fc27555d7591b7859404c5c3060df2b08bb53da120fb26da21cd42f0ac187a7bcc69e3b3964b26a2baecc5fe84e378aa7607c3d7675a37bb55c639379bb2700b1f8d3ec9903b3fbdba1b638ee0622e0b5273f579a9b0277d12ec72086cadd213a28835a98bcc5f3c01a9d99e76e319026db530fd6f7d0c6d43dd7583817e4ab92a9eb770638810461b58ef91f00d10f0c792fdfe485451cf28a3e1efdb4791f1dd4b2ae9853ad70c6eb4bfbfddd00a8e1619b18cfc525905cb67b1347d1eb45f783bee1053d86ec08d233923caecab30702f19bb37da8fe198bde4c3de7c84bd1df7d57cadd134b70e0dba527b479b33ddff354be444e7a1b57544a6e4b1be65da19351b6b432835bca9eab3ffc8bc46a9b0efbd86130d4024cfc26f70779934d030fd820c9c22e217fb6267bd9b8237c0e' 5 | Tag: b'04040404040404040404040404040404' 6 | --------------------------------------------------------------------------------