From 1fa7a1bd7c6b6665a9a09dca64eea78ed0f31643 Mon Sep 17 00:00:00 2001 From: zer0x64 Date: Wed, 20 Nov 2024 19:58:33 -0500 Subject: [PATCH 01/23] [XTS] Non-code files --- Cargo.lock | 125 ++++++++++++++++++++++------ Cargo.toml | 2 +- xts/CHANGELOG.md | 11 +++ xts/Cargo.toml | 31 +++++++ xts/LICENSE-APACHE | 201 +++++++++++++++++++++++++++++++++++++++++++++ xts/LICENSE-MIT | 25 ++++++ xts/src/README.md | 58 +++++++++++++ xts/src/lib.rs | 14 ++++ 8 files changed, 442 insertions(+), 25 deletions(-) create mode 100644 xts/CHANGELOG.md create mode 100644 xts/Cargo.toml create mode 100644 xts/LICENSE-APACHE create mode 100644 xts/LICENSE-MIT create mode 100644 xts/src/README.md create mode 100644 xts/src/lib.rs diff --git a/Cargo.lock b/Cargo.lock index fec7cd7..cb35240 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,6 +2,17 @@ # It is not intended for manual editing. version = 3 +[[package]] +name = "aes" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b169f7a6d4742236a0a00c541b845991d0ac43e546831af1249753ab4c3aa3a0" +dependencies = [ + "cfg-if", + "cipher 0.4.4", + "cpufeatures", +] + [[package]] name = "aes" version = "0.9.0-pre.2" @@ -9,7 +20,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e7856582c758ade85d71daf27ec6bcea6c1c73913692b07b8dffea2dc03531c9" dependencies = [ "cfg-if", - "cipher", + "cipher 0.5.0-pre.7", "cpufeatures", ] @@ -19,7 +30,7 @@ version = "0.2.0-pre.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6df0a14d60362d7b6041d3fe85dfd787ced16c4928f93d72152142c67d01d0bc" dependencies = [ - "cipher", + "cipher 0.5.0-pre.7", ] [[package]] @@ -27,7 +38,7 @@ name = "belt-ctr" version = "0.2.0-pre" dependencies = [ "belt-block", - "cipher", + "cipher 0.5.0-pre.7", "hex-literal", ] @@ -37,6 +48,15 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "847495c209977a90e8aad588b959d0ca9f5dc228096d29a6bd3defd53f35eaec" +[[package]] +name = "block-padding" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8894febbff9f758034a5b8e12d87918f56dfc64a8e1fe757d65e29041538d93" +dependencies = [ + "generic-array", +] + [[package]] name = "block-padding" version = "0.4.0-rc.0" @@ -50,8 +70,8 @@ dependencies = [ name = "cbc" version = "0.2.0-pre.2" dependencies = [ - "aes", - "cipher", + "aes 0.9.0-pre.2", + "cipher 0.5.0-pre.7", "hex-literal", ] @@ -59,9 +79,9 @@ dependencies = [ name = "cfb-mode" version = "0.9.0-pre" dependencies = [ - "aes", + "aes 0.9.0-pre.2", "belt-block", - "cipher", + "cipher 0.5.0-pre.7", "hex-literal", ] @@ -69,8 +89,8 @@ dependencies = [ name = "cfb8" version = "0.9.0-pre" dependencies = [ - "aes", - "cipher", + "aes 0.9.0-pre.2", + "cipher 0.5.0-pre.7", "hex-literal", ] @@ -80,6 +100,18 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +[[package]] +name = "cipher" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "773f3b9af64447d2ce9850330c473515014aa235e6a783b02db81ff39e4a3dad" +dependencies = [ + "blobby", + "crypto-common 0.1.6", + "inout 0.1.3", + "zeroize", +] + [[package]] name = "cipher" version = "0.5.0-pre.7" @@ -87,8 +119,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5b1425e6ce000f05a73096556cabcfb6a10a3ffe3bb4d75416ca8f00819c0b6a" dependencies = [ "blobby", - "crypto-common", - "inout", + "crypto-common 0.2.0-rc.1", + "inout 0.2.0-rc.0", "zeroize", ] @@ -101,6 +133,16 @@ dependencies = [ "libc", ] +[[package]] +name = "crypto-common" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" +dependencies = [ + "generic-array", + "typenum", +] + [[package]] name = "crypto-common" version = "0.2.0-rc.1" @@ -116,8 +158,8 @@ dependencies = [ name = "ctr" version = "0.10.0-pre.2" dependencies = [ - "aes", - "cipher", + "aes 0.9.0-pre.2", + "cipher 0.5.0-pre.7", "hex-literal", "kuznyechik", "magma", @@ -127,12 +169,22 @@ dependencies = [ name = "cts" version = "0.6.0" dependencies = [ - "aes", + "aes 0.9.0-pre.2", "belt-block", - "cipher", + "cipher 0.5.0-pre.7", "hex-literal", ] +[[package]] +name = "generic-array" +version = "0.14.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" +dependencies = [ + "typenum", + "version_check", +] + [[package]] name = "getrandom" version = "0.2.15" @@ -163,18 +215,28 @@ dependencies = [ name = "ige" version = "0.2.0-pre" dependencies = [ - "aes", - "cipher", + "aes 0.9.0-pre.2", + "cipher 0.5.0-pre.7", "hex-literal", ] +[[package]] +name = "inout" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a0c10553d664a4d0bcff9f4215d0aac67a639cc68ef660840afe309b807bc9f5" +dependencies = [ + "block-padding 0.3.3", + "generic-array", +] + [[package]] name = "inout" version = "0.2.0-rc.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bbc33218cf9ce7b927426ee4ad3501bcc5d8c26bf5fb4a82849a083715aca427" dependencies = [ - "block-padding", + "block-padding 0.4.0-rc.0", "hybrid-array", ] @@ -185,7 +247,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bd136ca56393426d0aeca01643adef06b1f30d667fdebf31f7cf11724cbd9051" dependencies = [ "cfg-if", - "cipher", + "cipher 0.5.0-pre.7", ] [[package]] @@ -200,15 +262,15 @@ version = "0.10.0-pre.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b9fd466f879a2c01f141f168c4e045a26dca5a60aef79efa8bb96e9c701bb8ea" dependencies = [ - "cipher", + "cipher 0.5.0-pre.7", ] [[package]] name = "ofb" version = "0.7.0-pre" dependencies = [ - "aes", - "cipher", + "aes 0.9.0-pre.2", + "cipher 0.5.0-pre.7", "hex-literal", ] @@ -216,8 +278,8 @@ dependencies = [ name = "pcbc" version = "0.2.0-pre" dependencies = [ - "aes", - "cipher", + "aes 0.9.0-pre.2", + "cipher 0.5.0-pre.7", "hex-literal", ] @@ -236,12 +298,27 @@ version = "1.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" +[[package]] +name = "version_check" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" + [[package]] name = "wasi" version = "0.11.0+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" +[[package]] +name = "xts" +version = "0.1.0" +dependencies = [ + "aes 0.8.4", + "cipher 0.4.4", + "hex-literal", +] + [[package]] name = "zeroize" version = "1.8.1" diff --git a/Cargo.toml b/Cargo.toml index adffbe5..432a71c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [workspace] resolver = "2" -members = ["belt-ctr", "cbc", "cts", "cfb8", "cfb-mode", "ctr", "ige", "ofb", "pcbc"] +members = ["belt-ctr", "cbc", "cts", "cfb8", "cfb-mode", "ctr", "ige", "ofb", "pcbc", "xts"] [profile.dev] opt-level = 2 diff --git a/xts/CHANGELOG.md b/xts/CHANGELOG.md new file mode 100644 index 0000000..e5d4046 --- /dev/null +++ b/xts/CHANGELOG.md @@ -0,0 +1,11 @@ +# Changelog + +All notable changes to this project will be documented in this file. + +The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), +and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). + +## 0.1.0 (2022-02-10) +- Initial release ([#TODO]) + +[#TODO]: https://github.com/RustCrypto/block-modes/pull/TODO \ No newline at end of file diff --git a/xts/Cargo.toml b/xts/Cargo.toml new file mode 100644 index 0000000..7968905 --- /dev/null +++ b/xts/Cargo.toml @@ -0,0 +1,31 @@ +[package] +name = "xts" +version = "0.1.0" +description = "Xor-Encrypt-Xor Tweaked-codebook with ciphertext Stealing (XTS) block cipher mode of operation" +authors = ["zer0x64"] +license = "MIT OR Apache-2.0" +edition = "2021" +rust-version = "1.81" +readme = "README.md" +documentation = "https://docs.rs/xts" +repository = "https://github.com/RustCrypto/block-modes" +keywords = ["crypto", "block-mode", "ciphers"] +categories = ["cryptography", "no-std"] + +[dependencies] +cipher = "0.4" + +[dev-dependencies] +aes = "0.8" +cipher = { version = "0.4", features = ["dev"] } +hex-literal = "0.4" + +[features] +default = [] +alloc = ["cipher/alloc"] +std = ["cipher/std", "alloc"] +zeroize = ["cipher/zeroize"] + +[package.metadata.docs.rs] +all-features = true +rustdoc-args = ["--cfg", "docsrs"] diff --git a/xts/LICENSE-APACHE b/xts/LICENSE-APACHE new file mode 100644 index 0000000..54891b3 --- /dev/null +++ b/xts/LICENSE-APACHE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + +Copyright 2024 zer0x64 + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/xts/LICENSE-MIT b/xts/LICENSE-MIT new file mode 100644 index 0000000..676061d --- /dev/null +++ b/xts/LICENSE-MIT @@ -0,0 +1,25 @@ +Copyright (c) 2024 zer0x64 + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. diff --git a/xts/src/README.md b/xts/src/README.md new file mode 100644 index 0000000..8e00072 --- /dev/null +++ b/xts/src/README.md @@ -0,0 +1,58 @@ +# RustCrypto: XTS + +[![crate][crate-image]][crate-link] +[![Docs][docs-image]][docs-link] +![Apache2/MIT licensed][license-image] +![Rust Version][rustc-image] +[![Project Chat][chat-image]][chat-link] +[![Build Status][build-image]][build-link] + +Generic implementation of the [Xor-encrypt-xor Tweaked-codebook with ciphertext Stealing][XTS] (XTS) block cipher +mode of operation. + +See [documentation][cipher-doc] of the `cipher` crate for additional information. + +## Minimum Supported Rust Version + +Rust **1.81** or higher. + +Minimum supported Rust version can be changed in the future, but it will be +done with a minor version bump. + +## SemVer Policy + +- All on-by-default features of this library are covered by SemVer +- MSRV is considered exempt from SemVer as noted above + +## License + +Licensed under either of: + + * [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0) + * [MIT license](http://opensource.org/licenses/MIT) + +at your option. + +### Contribution + +Unless you explicitly state otherwise, any contribution intentionally submitted +for inclusion in the work by you, as defined in the Apache-2.0 license, shall be +dual licensed as above, without any additional terms or conditions. + +[//]: # (badges) + +[crate-image]: https://img.shields.io/crates/v/xts.svg +[crate-link]: https://crates.io/crates/xts +[docs-image]: https://docs.rs/xts/badge.svg +[docs-link]: https://docs.rs/xts/ +[license-image]: https://img.shields.io/badge/license-Apache2.0/MIT-blue.svg +[rustc-image]: https://img.shields.io/badge/rustc-1.81+-blue.svg +[chat-image]: https://img.shields.io/badge/zulip-join_chat-blue.svg +[chat-link]: https://rustcrypto.zulipchat.com/#narrow/stream/308460-block-modes +[build-image]: https://github.com/RustCrypto/block-modes/workflows/xts/badge.svg?branch=master&event=push +[build-link]: https://github.com/RustCrypto/block-modes/actions?query=workflow%3Axts+branch%3Amaster + +[//]: # (general links) + +[xts]: https://en.wikipedia.org/wiki/Disk_encryption_theory#XTSDisk_encryption_theory#XTS +[cipher-doc]: https://docs.rs/cipher/ diff --git a/xts/src/lib.rs b/xts/src/lib.rs new file mode 100644 index 0000000..b93cf3f --- /dev/null +++ b/xts/src/lib.rs @@ -0,0 +1,14 @@ +pub fn add(left: u64, right: u64) -> u64 { + left + right +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn it_works() { + let result = add(2, 2); + assert_eq!(result, 4); + } +} From 7e9830b8fc67133225000e53e98970f057acc7f3 Mon Sep 17 00:00:00 2001 From: zer0x64 Date: Thu, 21 Nov 2024 00:17:44 -0500 Subject: [PATCH 02/23] [XTS] Initial implementation --- Cargo.lock | 120 +++++--------------- xts/Cargo.toml | 6 +- xts/{src => }/README.md | 2 +- xts/src/decrypt.rs | 238 ++++++++++++++++++++++++++++++++++++++++ xts/src/encrypt.rs | 232 +++++++++++++++++++++++++++++++++++++++ xts/src/lib.rs | 114 +++++++++++++++++-- xts/src/xts_core.rs | 115 +++++++++++++++++++ 7 files changed, 719 insertions(+), 108 deletions(-) rename xts/{src => }/README.md (95%) create mode 100644 xts/src/decrypt.rs create mode 100644 xts/src/encrypt.rs create mode 100644 xts/src/xts_core.rs diff --git a/Cargo.lock b/Cargo.lock index cb35240..6b5b5da 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,17 +2,6 @@ # It is not intended for manual editing. version = 3 -[[package]] -name = "aes" -version = "0.8.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b169f7a6d4742236a0a00c541b845991d0ac43e546831af1249753ab4c3aa3a0" -dependencies = [ - "cfg-if", - "cipher 0.4.4", - "cpufeatures", -] - [[package]] name = "aes" version = "0.9.0-pre.2" @@ -20,7 +9,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e7856582c758ade85d71daf27ec6bcea6c1c73913692b07b8dffea2dc03531c9" dependencies = [ "cfg-if", - "cipher 0.5.0-pre.7", + "cipher", "cpufeatures", ] @@ -30,7 +19,7 @@ version = "0.2.0-pre.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6df0a14d60362d7b6041d3fe85dfd787ced16c4928f93d72152142c67d01d0bc" dependencies = [ - "cipher 0.5.0-pre.7", + "cipher", ] [[package]] @@ -38,7 +27,7 @@ name = "belt-ctr" version = "0.2.0-pre" dependencies = [ "belt-block", - "cipher 0.5.0-pre.7", + "cipher", "hex-literal", ] @@ -48,15 +37,6 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "847495c209977a90e8aad588b959d0ca9f5dc228096d29a6bd3defd53f35eaec" -[[package]] -name = "block-padding" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8894febbff9f758034a5b8e12d87918f56dfc64a8e1fe757d65e29041538d93" -dependencies = [ - "generic-array", -] - [[package]] name = "block-padding" version = "0.4.0-rc.0" @@ -70,8 +50,8 @@ dependencies = [ name = "cbc" version = "0.2.0-pre.2" dependencies = [ - "aes 0.9.0-pre.2", - "cipher 0.5.0-pre.7", + "aes", + "cipher", "hex-literal", ] @@ -79,9 +59,9 @@ dependencies = [ name = "cfb-mode" version = "0.9.0-pre" dependencies = [ - "aes 0.9.0-pre.2", + "aes", "belt-block", - "cipher 0.5.0-pre.7", + "cipher", "hex-literal", ] @@ -89,8 +69,8 @@ dependencies = [ name = "cfb8" version = "0.9.0-pre" dependencies = [ - "aes 0.9.0-pre.2", - "cipher 0.5.0-pre.7", + "aes", + "cipher", "hex-literal", ] @@ -100,18 +80,6 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" -[[package]] -name = "cipher" -version = "0.4.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "773f3b9af64447d2ce9850330c473515014aa235e6a783b02db81ff39e4a3dad" -dependencies = [ - "blobby", - "crypto-common 0.1.6", - "inout 0.1.3", - "zeroize", -] - [[package]] name = "cipher" version = "0.5.0-pre.7" @@ -119,8 +87,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5b1425e6ce000f05a73096556cabcfb6a10a3ffe3bb4d75416ca8f00819c0b6a" dependencies = [ "blobby", - "crypto-common 0.2.0-rc.1", - "inout 0.2.0-rc.0", + "crypto-common", + "inout", "zeroize", ] @@ -133,16 +101,6 @@ dependencies = [ "libc", ] -[[package]] -name = "crypto-common" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" -dependencies = [ - "generic-array", - "typenum", -] - [[package]] name = "crypto-common" version = "0.2.0-rc.1" @@ -158,8 +116,8 @@ dependencies = [ name = "ctr" version = "0.10.0-pre.2" dependencies = [ - "aes 0.9.0-pre.2", - "cipher 0.5.0-pre.7", + "aes", + "cipher", "hex-literal", "kuznyechik", "magma", @@ -169,22 +127,12 @@ dependencies = [ name = "cts" version = "0.6.0" dependencies = [ - "aes 0.9.0-pre.2", + "aes", "belt-block", - "cipher 0.5.0-pre.7", + "cipher", "hex-literal", ] -[[package]] -name = "generic-array" -version = "0.14.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" -dependencies = [ - "typenum", - "version_check", -] - [[package]] name = "getrandom" version = "0.2.15" @@ -215,28 +163,18 @@ dependencies = [ name = "ige" version = "0.2.0-pre" dependencies = [ - "aes 0.9.0-pre.2", - "cipher 0.5.0-pre.7", + "aes", + "cipher", "hex-literal", ] -[[package]] -name = "inout" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a0c10553d664a4d0bcff9f4215d0aac67a639cc68ef660840afe309b807bc9f5" -dependencies = [ - "block-padding 0.3.3", - "generic-array", -] - [[package]] name = "inout" version = "0.2.0-rc.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bbc33218cf9ce7b927426ee4ad3501bcc5d8c26bf5fb4a82849a083715aca427" dependencies = [ - "block-padding 0.4.0-rc.0", + "block-padding", "hybrid-array", ] @@ -247,7 +185,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bd136ca56393426d0aeca01643adef06b1f30d667fdebf31f7cf11724cbd9051" dependencies = [ "cfg-if", - "cipher 0.5.0-pre.7", + "cipher", ] [[package]] @@ -262,15 +200,15 @@ version = "0.10.0-pre.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b9fd466f879a2c01f141f168c4e045a26dca5a60aef79efa8bb96e9c701bb8ea" dependencies = [ - "cipher 0.5.0-pre.7", + "cipher", ] [[package]] name = "ofb" version = "0.7.0-pre" dependencies = [ - "aes 0.9.0-pre.2", - "cipher 0.5.0-pre.7", + "aes", + "cipher", "hex-literal", ] @@ -278,8 +216,8 @@ dependencies = [ name = "pcbc" version = "0.2.0-pre" dependencies = [ - "aes 0.9.0-pre.2", - "cipher 0.5.0-pre.7", + "aes", + "cipher", "hex-literal", ] @@ -298,12 +236,6 @@ version = "1.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" -[[package]] -name = "version_check" -version = "0.9.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" - [[package]] name = "wasi" version = "0.11.0+wasi-snapshot-preview1" @@ -314,8 +246,8 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" name = "xts" version = "0.1.0" dependencies = [ - "aes 0.8.4", - "cipher 0.4.4", + "aes", + "cipher", "hex-literal", ] diff --git a/xts/Cargo.toml b/xts/Cargo.toml index 7968905..e51a85d 100644 --- a/xts/Cargo.toml +++ b/xts/Cargo.toml @@ -13,11 +13,11 @@ keywords = ["crypto", "block-mode", "ciphers"] categories = ["cryptography", "no-std"] [dependencies] -cipher = "0.4" +cipher = "=0.5.0-pre.7" [dev-dependencies] -aes = "0.8" -cipher = { version = "0.4", features = ["dev"] } +aes = "=0.9.0-pre.2" +cipher = { version = "=0.5.0-pre.7", features = ["dev"] } hex-literal = "0.4" [features] diff --git a/xts/src/README.md b/xts/README.md similarity index 95% rename from xts/src/README.md rename to xts/README.md index 8e00072..d758974 100644 --- a/xts/src/README.md +++ b/xts/README.md @@ -54,5 +54,5 @@ dual licensed as above, without any additional terms or conditions. [//]: # (general links) -[xts]: https://en.wikipedia.org/wiki/Disk_encryption_theory#XTSDisk_encryption_theory#XTS +[xts]: https://en.wikipedia.org/wiki/Disk_encryption_theory#XTS [cipher-doc]: https://docs.rs/cipher/ diff --git a/xts/src/decrypt.rs b/xts/src/decrypt.rs new file mode 100644 index 0000000..b6d56b3 --- /dev/null +++ b/xts/src/decrypt.rs @@ -0,0 +1,238 @@ +use crate::xts_core::{precompute_iv, Xts}; +use cipher::{ + array::Array, + crypto_common::{BlockSizes, InnerUser, IvSizeUser}, + inout::InOut, + AlgorithmName, Block, BlockCipherDecBackend, BlockCipherDecClosure, BlockCipherDecrypt, + BlockCipherEncrypt, BlockModeDecBackend, BlockModeDecClosure, BlockModeDecrypt, BlockSizeUser, + InnerIvInit, Iv, IvState, KeyInit, ParBlocks, ParBlocksSizeUser, +}; +use core::fmt; + +#[cfg(feature = "zeroize")] +use cipher::zeroize::{Zeroize, ZeroizeOnDrop}; + +/// XTS mode decryptor. +#[derive(Clone)] +pub struct Decryptor +where + C: BlockCipherDecrypt, +{ + cipher: C, + iv: Block, +} + +// This would probably be the cleanest way to do it, but it would require a way to multiply a typenum by 2 +// impl KeySizeUser for Decryptor where C: KeySizeUser { +// type KeySize = C::KeySize * 2; +// } + +impl Decryptor +where + BS: BlockSizes, + C: BlockCipherDecrypt + KeyInit, +{ + /// Create an XTS array and precompute it + pub fn new_xts( + k1: Array, + k2: Array, + mut iv: Block, + ) -> Self + where + E: BlockCipherEncrypt + KeyInit, + { + let tweaker = E::new(&k2); + precompute_iv(&tweaker, &mut iv); + + let cipher = C::new(&k1); + + Self { cipher, iv } + } +} + +impl BlockSizeUser for Decryptor +where + C: BlockCipherDecrypt, +{ + type BlockSize = C::BlockSize; +} + +impl BlockModeDecrypt for Decryptor +where + C: BlockCipherDecrypt, +{ + fn decrypt_with_backend(&mut self, f: impl BlockModeDecClosure) { + struct Closure<'a, BS, BC> + where + BS: BlockSizes, + BC: BlockModeDecClosure, + { + iv: &'a mut Array, + f: BC, + } + + impl BlockSizeUser for Closure<'_, BS, BC> + where + BS: BlockSizes, + BC: BlockModeDecClosure, + { + type BlockSize = BS; + } + + impl BlockCipherDecClosure for Closure<'_, BS, BC> + where + BS: BlockSizes, + BC: BlockModeDecClosure, + { + #[inline(always)] + fn call>( + self, + cipher_backend: &B, + ) { + let Self { iv, f } = self; + f.call(&mut Backend { iv, cipher_backend }); + } + } + + let Self { cipher, iv } = self; + cipher.decrypt_with_backend(Closure { iv, f }) + } +} + +impl InnerUser for Decryptor +where + C: BlockCipherDecrypt, +{ + type Inner = C; +} + +impl IvSizeUser for Decryptor +where + C: BlockCipherDecrypt, +{ + type IvSize = C::BlockSize; +} + +impl InnerIvInit for Decryptor +where + C: BlockCipherDecrypt, +{ + #[inline] + fn inner_iv_init(cipher: C, iv: &Iv) -> Self { + Self { + cipher, + iv: iv.clone(), + } + } +} + +impl IvState for Decryptor +where + C: BlockCipherDecrypt, +{ + #[inline] + fn iv_state(&self) -> Iv { + self.iv.clone() + } +} + +impl AlgorithmName for Decryptor +where + C: BlockCipherDecrypt + AlgorithmName, +{ + fn write_alg_name(f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str("xts::Decryptor<")?; + ::write_alg_name(f)?; + f.write_str(">") + } +} + +impl fmt::Debug for Decryptor +where + C: BlockCipherDecrypt + AlgorithmName, +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str("xts::Decryptor<")?; + ::write_alg_name(f)?; + f.write_str("> { ... }") + } +} + +impl Drop for Decryptor { + fn drop(&mut self) { + #[cfg(feature = "zeroize")] + self.iv.zeroize(); + } +} + +#[cfg(feature = "zeroize")] +impl ZeroizeOnDrop for Decryptor {} + +struct Backend<'a, BS, BK> +where + BS: BlockSizes, + BK: BlockCipherDecBackend, +{ + iv: &'a mut Array, + cipher_backend: &'a BK, +} + +impl BlockSizeUser for Backend<'_, BS, BK> +where + BS: BlockSizes, + BK: BlockCipherDecBackend, +{ + type BlockSize = BS; +} + +impl ParBlocksSizeUser for Backend<'_, BS, BK> +where + BS: BlockSizes, + BK: BlockCipherDecBackend, +{ + type ParBlocksSize = BK::ParBlocksSize; +} + +impl BlockModeDecBackend for Backend<'_, BS, BK> +where + BS: BlockSizes, + BK: BlockCipherDecBackend, +{ + #[inline(always)] + fn decrypt_block(&mut self, block: InOut<'_, '_, Block>) { + self.process_block(block); + } + + #[inline(always)] + fn decrypt_par_blocks(&mut self, blocks: InOut<'_, '_, ParBlocks>) { + self.process_par_blocks(blocks); + } + + #[inline(always)] + fn decrypt_block_inplace(&mut self, block: &mut Block) { + self.process_block_inplace(block); + } + + #[inline(always)] + fn decrypt_par_blocks_inplace(&mut self, blocks: &mut ParBlocks) { + self.process_par_blocks_inplace(blocks); + } +} + +impl Xts for Backend<'_, BS, BC> +where + BS: BlockSizes, + BC: BlockCipherDecBackend, +{ + fn process_inplace(&self, block: &mut Block) { + self.cipher_backend.decrypt_block_inplace(block); + } + + fn process_par_inplace(&self, blocks: &mut ParBlocks) { + self.cipher_backend.decrypt_par_blocks_inplace(blocks); + } + + fn get_iv_mut(&mut self) -> &mut Block { + self.iv + } +} diff --git a/xts/src/encrypt.rs b/xts/src/encrypt.rs new file mode 100644 index 0000000..085dc1c --- /dev/null +++ b/xts/src/encrypt.rs @@ -0,0 +1,232 @@ +use crate::xts_core::{precompute_iv, Xts}; + +use cipher::{ + crypto_common::{BlockSizes, InnerUser}, + AlgorithmName, Array, Block, BlockCipherEncBackend, BlockCipherEncClosure, BlockCipherEncrypt, + BlockModeEncBackend, BlockModeEncClosure, BlockModeEncrypt, BlockSizeUser, InOut, InnerIvInit, + Iv, IvSizeUser, IvState, KeyInit, ParBlocks, ParBlocksSizeUser, +}; +use core::fmt; + +#[cfg(feature = "zeroize")] +use cipher::zeroize::{Zeroize, ZeroizeOnDrop}; + +/// XTS mode encryptor. +#[derive(Clone)] +pub struct Encryptor +where + C: BlockCipherEncrypt, +{ + cipher: C, + iv: Block, +} + +// This would probably be the cleanest way to do it, but it would require a way to multiply a typenum by 2 +// impl KeySizeUser for Encryptor where C: KeySizeUser { +// type KeySize = C::KeySize * 2; +// } + +impl Encryptor +where + C: BlockCipherEncrypt + KeyInit, +{ + /// Create an XTS array and precompute it + pub fn new_xts( + k1: Array, + k2: Array, + mut iv: Block, + ) -> Self { + let tweaker = C::new(&k2); + precompute_iv(&tweaker, &mut iv); + + let cipher = C::new(&k1); + + Self { cipher, iv } + } +} + +impl BlockSizeUser for Encryptor +where + C: BlockCipherEncrypt, +{ + type BlockSize = C::BlockSize; +} + +// Note: Needs to be removed if we want to override key size +impl InnerUser for Encryptor +where + C: BlockCipherEncrypt, +{ + type Inner = C; +} + +impl IvSizeUser for Encryptor +where + C: BlockCipherEncrypt, +{ + type IvSize = C::BlockSize; +} + +impl InnerIvInit for Encryptor +where + C: BlockCipherEncrypt, +{ + #[inline] + fn inner_iv_init(cipher: C, iv: &Iv) -> Self { + Self { + cipher, + iv: iv.clone(), + } + } +} + +impl IvState for Encryptor +where + C: BlockCipherEncrypt, +{ + #[inline] + fn iv_state(&self) -> Iv { + self.iv.clone() + } +} + +impl BlockModeEncrypt for Encryptor +where + C: BlockCipherEncrypt, +{ + fn encrypt_with_backend(&mut self, f: impl BlockModeEncClosure) { + struct Closure<'a, BS, BM> + where + BS: BlockSizes, + BM: BlockModeEncClosure, + { + iv: &'a mut Array, + f: BM, + } + + impl BlockSizeUser for Closure<'_, BS, BM> + where + BS: BlockSizes, + BM: BlockModeEncClosure, + { + type BlockSize = BS; + } + + impl BlockCipherEncClosure for Closure<'_, BS, BM> + where + BS: BlockSizes, + BM: BlockModeEncClosure, + { + #[inline(always)] + fn call>(self, cipher_backend: &B) { + let Self { iv, f } = self; + f.call(&mut Backend { iv, cipher_backend }); + } + } + + let Self { cipher, iv } = self; + let f = Closure { iv, f }; + cipher.encrypt_with_backend(f) + } +} + +impl AlgorithmName for Encryptor +where + C: BlockCipherEncrypt + AlgorithmName, +{ + fn write_alg_name(f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str("xts::Encryptor<")?; + ::write_alg_name(f)?; + f.write_str(">") + } +} + +impl fmt::Debug for Encryptor +where + C: BlockCipherEncrypt + AlgorithmName, +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str("xts::Encryptor<")?; + ::write_alg_name(f)?; + f.write_str("> { ... }") + } +} + +impl Drop for Encryptor { + fn drop(&mut self) { + #[cfg(feature = "zeroize")] + self.iv.zeroize(); + } +} + +#[cfg(feature = "zeroize")] +impl ZeroizeOnDrop for Encryptor {} + +struct Backend<'a, BS, BC> +where + BS: BlockSizes, + BC: BlockCipherEncBackend, +{ + iv: &'a mut Array, + cipher_backend: &'a BC, +} + +impl BlockSizeUser for Backend<'_, BS, BK> +where + BS: BlockSizes, + BK: BlockCipherEncBackend, +{ + type BlockSize = BS; +} + +impl ParBlocksSizeUser for Backend<'_, BS, BK> +where + BS: BlockSizes, + BK: BlockCipherEncBackend, +{ + type ParBlocksSize = BK::ParBlocksSize; +} + +impl BlockModeEncBackend for Backend<'_, BS, BK> +where + BS: BlockSizes, + BK: BlockCipherEncBackend, +{ + #[inline(always)] + fn encrypt_block(&mut self, block: InOut<'_, '_, Block>) { + self.process_block(block); + } + + #[inline(always)] + fn encrypt_par_blocks(&mut self, blocks: InOut<'_, '_, ParBlocks>) { + self.process_par_blocks(blocks); + } + + #[inline(always)] + fn encrypt_block_inplace(&mut self, block: &mut Block) { + self.process_block_inplace(block); + } + + #[inline(always)] + fn encrypt_par_blocks_inplace(&mut self, blocks: &mut ParBlocks) { + self.process_par_blocks_inplace(blocks); + } +} + +impl Xts for Backend<'_, BS, BC> +where + BS: BlockSizes, + BC: BlockCipherEncBackend, +{ + fn process_inplace(&self, block: &mut Block) { + self.cipher_backend.encrypt_block_inplace(block); + } + + fn process_par_inplace(&self, blocks: &mut ParBlocks) { + self.cipher_backend.encrypt_par_blocks_inplace(blocks); + } + + fn get_iv_mut(&mut self) -> &mut Block { + self.iv + } +} diff --git a/xts/src/lib.rs b/xts/src/lib.rs index b93cf3f..ee5f4fc 100644 --- a/xts/src/lib.rs +++ b/xts/src/lib.rs @@ -1,14 +1,108 @@ -pub fn add(left: u64, right: u64) -> u64 { - left + right -} +//! [Xor-Encrypt-Xor Tweaked-codebook with ciphertext Stealing][1] (XTS) mode. +//! +//! Mode functionality is accessed using traits from re-exported [`cipher`] crate. +//! +//! # ⚠️ Security Warning: Hazmat! +//! +//! This crate does not ensure ciphertexts are authentic! Thus ciphertext integrity +//! is not verified, which can lead to serious vulnerabilities! +//! [AEADs][https://github.com/RustCrypto/AEADs] provide simple authenticated encryption, +//! which is much less error-prone than manual integrity verification. +//! +//! # Example +//! ``` +//! use aes::cipher::{BlockModeEncrypt, BlockModeDecrypt, KeyIvInit}; +//! use hex_literal::hex; +//! +//! type Aes128XtsEnc = xts::Encryptor; +//! type Aes128XtsDec = xts::Decryptor; +//! +//! let key = [0x42; 32]; +//! let tweak = [0x24; 16]; +//! let plaintext = *b"hello world! this is my plaintext."; +//! let ciphertext = hex!( // TODO fix this +//! "c7fe247ef97b21f07cbdd26cb5d346bf" +//! "d27867cb00d9486723e159978fb9a5f9" +//! "14cfb228a710de4171e396e7b6cf859e" +//! ); +//! +//! // encrypt/decrypt in-place +//! // buffer must be big enough for padded plaintext +//! let pt_len = plaintext.len(); +//! let mut buf = [0u8; pt_len]; +//! buf.copy_from_slice(&plaintext); +//! Aes128XtsEnc::new(&key.into(), &tweak.into()) +//! .encrypt_blocks(&mut buf) +//! .unwrap(); +//! assert_eq!(&buf, &ciphertext); +//! +//! Aes128XtsDec::new(&key.into(), &iv.into()) +//! .decrypt_blocks(&mut buf) +//! .unwrap(); +//! assert_eq!(&buf, &plaintext); +//! +//! // encrypt/decrypt from buffer to buffer +//! let mut buf = [0u8; pt_len]; +//! Aes128XtsEnc::new(&key.into(), &iv.into()) +//! .encrypt_blocks_b2b(&plaintext, &mut buf) +//! .unwrap(); +//! assert_eq!(&buf, &ciphertext); +//! +//! let pt = Aes128XtsDec::new(&key.into(), &iv.into()) +//! .decrypt_blocks_b2b(&ct, &mut buf) +//! .unwrap(); +//! assert_eq!(&buf, &plaintext); +//! ``` +//! +//! With enabled `alloc` (or `std`) feature you also can use allocating +//! convenience methods: +//! NOTE FOR REVIEWER: Aren't we missing a `encrypt_blocks_vec` method or something in the cipher crate? +//! ``` +//! # use aes::cipher::{BlockModeEncrypt, BlockModeDecrypt, KeyIvInit}; +//! # use hex_literal::hex; +//! # type Aes128XtsEnc = xts::Encryptor; +//! # type Aes128XtsDec = xts::Decryptor; +//! # let key = [0x42; 32]; +//! # let iv = [0x24; 16]; +//! # let plaintext = *b"hello world! this is my plaintext."; +//! # let ciphertext = hex!( +//! # "c7fe247ef97b21f07cbdd26cb5d346bf" +//! # "d27867cb00d9486723e159978fb9a5f9" +//! # "14cfb228a710de4171e396e7b6cf859e" +//! # ); +//! // let res = Aes128CbcEnc::new(&key.into(), &iv.into()) +//! // .encrypt_blocks_vec(&plaintext); +//! // assert_eq!(res[..], ciphertext[..]); +//! // let res = Aes128CbcDec::new(&key.into(), &iv.into()) +//! // .decrypt_padded_vec::(&res) +//! // .unwrap(); +//! // assert_eq!(res[..], plaintext[..]); +//! # } +//! ``` +//! +//! [1]: https://en.wikipedia.org/wiki/Disk_encryption_theory#XTS + +#![no_std] +#![doc( + html_logo_url = "https://raw.githubusercontent.com/RustCrypto/media/6ee8e381/logo.svg", + html_favicon_url = "https://raw.githubusercontent.com/RustCrypto/media/6ee8e381/logo.svg" +)] +#![forbid(unsafe_code)] +#![cfg_attr(docsrs, feature(doc_auto_cfg))] +#![warn(missing_debug_implementations, missing_docs, rust_2018_idioms)] + +use cipher::array::{Array, ArraySize}; + +mod decrypt; +mod encrypt; +mod xts_core; -#[cfg(test)] -mod tests { - use super::*; +pub use decrypt::Decryptor; +pub use encrypt::Encryptor; - #[test] - fn it_works() { - let result = add(2, 2); - assert_eq!(result, 4); +#[inline(always)] +fn xor(out: &mut Array, buf: &Array) { + for (a, b) in out.iter_mut().zip(buf) { + *a ^= *b; } } diff --git a/xts/src/xts_core.rs b/xts/src/xts_core.rs new file mode 100644 index 0000000..6f2eadd --- /dev/null +++ b/xts/src/xts_core.rs @@ -0,0 +1,115 @@ +use cipher::{ + crypto_common::BlockSizes, Array, Block, BlockCipherEncrypt, BlockSizeUser, InOut, ParBlocks, + ParBlocksSizeUser, +}; + +use crate::xor; + +/// Derived from the polynomial x^128 + x^5 + x + 1 +const GF_MOD: u8 = 0x87; + +/// Since the traits does not allow using two engines, this is used to pre-compute the IV. +pub fn precompute_iv(cipher: &BC, iv: &mut Array) +where + BS: BlockSizes, + BC: BlockCipherEncrypt, +{ + cipher.encrypt_block(iv); +} + +pub fn gf_mul(tweak: &mut Array) -> bool +where + BS: BlockSizes, +{ + let mut carry = 0; + + for i in 0..BS::to_usize() { + // Save carry from previous byte + let old_carry = carry; + + // Check if there is a carry for this shift + carry = (tweak[i] & 0x80) >> 7; + + // Shift left + tweak[i] <<= 1; + + // Carry over bit from last carry + tweak[i] |= old_carry; + } + + // If there is a carry, we mod by the polynomial + if carry == 1 { + tweak[0] ^= GF_MOD; + } + + carry == 1 +} + +/// Core implementation of XTS mode +pub trait Xts: ParBlocksSizeUser + BlockSizeUser { + /// Method to encrypt/decrypt a single block without mode. + fn process_inplace(&self, block: &mut Block); // Array); + + /// Method to encrypt/decrypt multiple blocks in parallel without mode. + fn process_par_inplace(&self, blocks: &mut ParBlocks); + + /// Gets the IV reference. + fn get_iv_mut(&mut self) -> &mut Array; + + // Unused but keeping for now just in case + // fn process(&self, mut block: InOut<'_, '_, Block>) { + // let mut b = block.clone_in(); + // self.process_inplace(&mut b); + + // *block.get_out() = b; + // } + + // fn process_par(&self, blocks: InOut<'_, '_, ParBlocks>) { + // let mut blocks = blocks.clone_in(); + // self.process_par_inplace(&mut blocks); + // } + + fn process_block_inplace(&mut self, block: &mut Block) { + { + let iv = self.get_iv_mut(); + xor(block, iv); + } + + self.process_inplace(block); + + let iv = self.get_iv_mut(); + xor(block, iv); + + let _ = gf_mul(iv); + } + + /// Encrypt/decrypt a block using XTS and update the tweak + fn process_block(&mut self, mut block: InOut<'_, '_, Array>) { + let mut b = block.clone_in(); + self.process_block_inplace(&mut b); + + *block.get_out() = b; + } + + /// Encrypt/decrypt multiple blocks in parrallel using XTS and update the tweak + fn process_par_blocks_inplace(&mut self, blocks: &mut ParBlocks) { + { + let iv = self.get_iv_mut(); + + for b in blocks.iter_mut() { + xor(b, iv); + + let _ = gf_mul(iv); + } + } + + self.process_par_inplace(blocks); + } + + fn process_par_blocks(&mut self, mut block: InOut<'_, '_, ParBlocks>) { + let mut b = block.clone_in(); + self.process_par_blocks_inplace(&mut b); + + *block.get_out() = b; + } +} From 5a381a2c37580d2b49bc9ce614187320e4e5d73f Mon Sep 17 00:00:00 2001 From: zer0x64 Date: Thu, 21 Nov 2024 01:06:47 -0500 Subject: [PATCH 03/23] [XTS] Implemented parallel tail processing --- xts/src/decrypt.rs | 10 ++++++++++ xts/src/encrypt.rs | 10 ++++++++++ xts/src/xts_core.rs | 42 ++++++++++++++++++++++++++++++++++++------ 3 files changed, 56 insertions(+), 6 deletions(-) diff --git a/xts/src/decrypt.rs b/xts/src/decrypt.rs index b6d56b3..9863e6a 100644 --- a/xts/src/decrypt.rs +++ b/xts/src/decrypt.rs @@ -217,6 +217,16 @@ where fn decrypt_par_blocks_inplace(&mut self, blocks: &mut ParBlocks) { self.process_par_blocks_inplace(blocks); } + + #[inline(always)] + fn decrypt_tail_blocks(&mut self, blocks: cipher::InOutBuf<'_, '_, Block>) { + self.process_tail_blocks(blocks); + } + + #[inline(always)] + fn decrypt_tail_blocks_inplace(&mut self, blocks: &mut [Block]) { + self.process_tail_blocks_inplace(blocks); + } } impl Xts for Backend<'_, BS, BC> diff --git a/xts/src/encrypt.rs b/xts/src/encrypt.rs index 085dc1c..7931a9c 100644 --- a/xts/src/encrypt.rs +++ b/xts/src/encrypt.rs @@ -211,6 +211,16 @@ where fn encrypt_par_blocks_inplace(&mut self, blocks: &mut ParBlocks) { self.process_par_blocks_inplace(blocks); } + + #[inline(always)] + fn encrypt_tail_blocks(&mut self, blocks: cipher::InOutBuf<'_, '_, Block>) { + self.process_tail_blocks(blocks); + } + + #[inline(always)] + fn encrypt_tail_blocks_inplace(&mut self, blocks: &mut [Block]) { + self.process_tail_blocks_inplace(blocks); + } } impl Xts for Backend<'_, BS, BC> diff --git a/xts/src/xts_core.rs b/xts/src/xts_core.rs index 6f2eadd..52c97e3 100644 --- a/xts/src/xts_core.rs +++ b/xts/src/xts_core.rs @@ -1,6 +1,6 @@ use cipher::{ - crypto_common::BlockSizes, Array, Block, BlockCipherEncrypt, BlockSizeUser, InOut, ParBlocks, - ParBlocksSizeUser, + crypto_common::BlockSizes, Array, Block, BlockCipherEncrypt, BlockSizeUser, InOut, InOutBuf, + ParBlocks, ParBlocksSizeUser, }; use crate::xor; @@ -93,10 +93,12 @@ pub trait Xts: ParBlocksSizeUser + BlockSizeUser { /// Encrypt/decrypt multiple blocks in parrallel using XTS and update the tweak fn process_par_blocks_inplace(&mut self, blocks: &mut ParBlocks) { + let mut iv_array: ParBlocks = Default::default(); { let iv = self.get_iv_mut(); - for b in blocks.iter_mut() { + for (b, i) in blocks.iter_mut().zip(iv_array.iter_mut()) { + *i = iv.clone(); xor(b, iv); let _ = gf_mul(iv); @@ -104,12 +106,40 @@ pub trait Xts: ParBlocksSizeUser + BlockSizeUser { } self.process_par_inplace(blocks); + + for (b, i) in blocks.iter_mut().zip(iv_array.iter_mut()) { + xor(b, i); + } } - fn process_par_blocks(&mut self, mut block: InOut<'_, '_, ParBlocks>) { - let mut b = block.clone_in(); + fn process_par_blocks(&mut self, mut blocks: InOut<'_, '_, ParBlocks>) { + let mut b = blocks.clone_in(); self.process_par_blocks_inplace(&mut b); - *block.get_out() = b; + *blocks.get_out() = b; + } + + fn process_tail_blocks_inplace(&mut self, blocks: &mut [Block]) { + for b in blocks { + { + let iv = self.get_iv_mut(); + xor(b, iv); + } + + self.process_block_inplace(b); + + let iv = self.get_iv_mut(); + xor(b, iv); + + let _ = gf_mul(iv); + } + } + + fn process_tail_blocks(&mut self, blocks: InOutBuf<'_, '_, Block>) { + for mut block in blocks { + let mut b = block.clone_in(); + self.process_inplace(&mut b); + *block.get_out() = b; + } } } From c8e838faf5071494ac3eed3d92fc613a443b8f36 Mon Sep 17 00:00:00 2001 From: zer0x64 Date: Thu, 21 Nov 2024 01:46:54 -0500 Subject: [PATCH 04/23] [XTS] Implemented ciphertext stealing --- xts/src/decrypt.rs | 5 +++ xts/src/encrypt.rs | 5 +++ xts/src/xts_core.rs | 90 ++++++++++++++++++++++++++++++++++++++++++++- 3 files changed, 99 insertions(+), 1 deletion(-) diff --git a/xts/src/decrypt.rs b/xts/src/decrypt.rs index 9863e6a..68e6fc6 100644 --- a/xts/src/decrypt.rs +++ b/xts/src/decrypt.rs @@ -245,4 +245,9 @@ where fn get_iv_mut(&mut self) -> &mut Block { self.iv } + + #[inline(always)] + fn is_decrypt() -> bool { + true + } } diff --git a/xts/src/encrypt.rs b/xts/src/encrypt.rs index 7931a9c..d2a8f20 100644 --- a/xts/src/encrypt.rs +++ b/xts/src/encrypt.rs @@ -239,4 +239,9 @@ where fn get_iv_mut(&mut self) -> &mut Block { self.iv } + + #[inline(always)] + fn is_decrypt() -> bool { + false + } } diff --git a/xts/src/xts_core.rs b/xts/src/xts_core.rs index 52c97e3..ec78e2f 100644 --- a/xts/src/xts_core.rs +++ b/xts/src/xts_core.rs @@ -17,7 +17,7 @@ where cipher.encrypt_block(iv); } -pub fn gf_mul(tweak: &mut Array) -> bool +fn gf_mul(tweak: &mut Array) -> bool where BS: BlockSizes, { @@ -45,6 +45,38 @@ where carry == 1 } +// This is only used once when decrypting with ciphertext stealing +fn gf_reverse_mul(tweak: &mut Array, carry: bool) +where + BS: BlockSizes, +{ + if carry { + tweak[0] ^= GF_MOD; + } + + let mut new_carry = 0; + + for i in BS::to_usize() - 1..=0 { + // Save carry from previous byte + let old_carry = new_carry; + + // Check if there is a carry for this shift + new_carry = tweak[i] & 1; + + // Shift right + tweak[i] >>= 1; + + // Carry over bit from last carry + tweak[i] |= old_carry << 7; + } + + // If there is a carry, we mod by the polynomial + if carry { + let len = tweak.len(); + tweak[len - 1] |= 0x80; + } +} + /// Core implementation of XTS mode pub trait Xts: ParBlocksSizeUser + BlockSizeUser { /// Method to encrypt/decrypt a single block without mode. @@ -56,6 +88,9 @@ pub trait Xts: ParBlocksSizeUser + BlockSizeUser { /// Gets the IV reference. fn get_iv_mut(&mut self) -> &mut Array; + /// There is a slight difference regarding the tweak during decryption + fn is_decrypt() -> bool; + // Unused but keeping for now just in case // fn process(&self, mut block: InOut<'_, '_, Block>) { // let mut b = block.clone_in(); @@ -142,4 +177,57 @@ pub trait Xts: ParBlocksSizeUser + BlockSizeUser { *block.get_out() = b; } } + + fn ciphertext_stealing(&mut self, buffer: &mut [u8]) { + let remaining_bytes = buffer.len() - Self::block_size(); + + let need_stealing = remaining_bytes > 0; + + { + let second_to_last_block: &mut Block = (&mut buffer[0..Self::block_size()]) + .try_into() + .expect("Not a full block on second to last block!"); + + // This is a special case + if Self::is_decrypt() && need_stealing { + // We fast forward the multiplication here + + let carry = { + let mut iv = self.get_iv_mut(); + let carry = gf_mul(&mut iv); + + xor(second_to_last_block, iv); + + carry + }; + + self.process_inplace(second_to_last_block); + + { + let iv = self.get_iv_mut(); + xor(second_to_last_block, iv); + + gf_reverse_mul(iv, carry); + } + } else { + // Process normally + self.process_block_inplace(second_to_last_block); + } + } + + if need_stealing { + // We first swap the remaining bytes with the previous block's + { + let (previous_block, current_block) = buffer.split_at_mut(Self::block_size()); + previous_block[..remaining_bytes] + .swap_with_slice(&mut current_block[..remaining_bytes]); + } + + // We can then decrypt the final block, which is not located at the second to last block + let second_to_last_block: &mut Block = (&mut buffer[0..Self::block_size()]) + .try_into() + .expect("Not a full block on second to last block!"); + self.process_block_inplace(second_to_last_block); + } + } } From b3163aa99aa0f6a47929d2d964d77b67326e0603 Mon Sep 17 00:00:00 2001 From: zer0x64 Date: Fri, 22 Nov 2024 17:49:19 -0500 Subject: [PATCH 05/23] [XTS] implement KeySize correctly --- xts/src/decrypt.rs | 152 +++++++++++++++++++++++++++----------------- xts/src/encrypt.rs | 88 +++++++++++++------------ xts/src/xts_core.rs | 10 +-- 3 files changed, 149 insertions(+), 101 deletions(-) diff --git a/xts/src/decrypt.rs b/xts/src/decrypt.rs index 68e6fc6..c0f3dc1 100644 --- a/xts/src/decrypt.rs +++ b/xts/src/decrypt.rs @@ -1,65 +1,101 @@ use crate::xts_core::{precompute_iv, Xts}; use cipher::{ - array::Array, - crypto_common::{BlockSizes, InnerUser, IvSizeUser}, + array::{Array, ArraySize}, + consts::B1, + crypto_common::{BlockSizes, IvSizeUser}, inout::InOut, + typenum::Double, AlgorithmName, Block, BlockCipherDecBackend, BlockCipherDecClosure, BlockCipherDecrypt, BlockCipherEncrypt, BlockModeDecBackend, BlockModeDecClosure, BlockModeDecrypt, BlockSizeUser, - InnerIvInit, Iv, IvState, KeyInit, ParBlocks, ParBlocksSizeUser, + Iv, IvState, Key, KeyInit, KeyIvInit, KeySizeUser, ParBlocks, ParBlocksSizeUser, }; -use core::fmt; +use core::{fmt, ops::Shl}; #[cfg(feature = "zeroize")] use cipher::zeroize::{Zeroize, ZeroizeOnDrop}; /// XTS mode decryptor. #[derive(Clone)] -pub struct Decryptor +pub struct Decryptor where - C: BlockCipherDecrypt, + BS: BlockSizes, + C: BlockCipherDecrypt, + T: BlockCipherEncrypt, { cipher: C, + tweaker: T, iv: Block, } -// This would probably be the cleanest way to do it, but it would require a way to multiply a typenum by 2 -// impl KeySizeUser for Decryptor where C: KeySizeUser { -// type KeySize = C::KeySize * 2; -// } +impl KeySizeUser for Decryptor +where + KS: ArraySize + Shl, + >::Output: ArraySize, + BS: BlockSizes, + C: BlockCipherDecrypt + KeySizeUser, + T: BlockCipherEncrypt + KeySizeUser, +{ + type KeySize = Double; +} -impl Decryptor +impl KeyIvInit for Decryptor where + KS: ArraySize, BS: BlockSizes, - C: BlockCipherDecrypt + KeyInit, + C: BlockCipherDecrypt + KeySizeUser + KeyInit, + T: BlockCipherEncrypt + KeySizeUser + KeyInit, + Decryptor: KeySizeUser, { - /// Create an XTS array and precompute it - pub fn new_xts( - k1: Array, - k2: Array, - mut iv: Block, - ) -> Self - where - E: BlockCipherEncrypt + KeyInit, - { - let tweaker = E::new(&k2); - precompute_iv(&tweaker, &mut iv); + fn new(key: &Key, iv: &Iv) -> Self { + // Split the key and call split key constructor + let k1 = <&Key>::try_from(&key[..C::key_size()]) + .expect("Due to trait bounds, k1 should always be half the size of the XTS key"); + let k2 = <&Key>::try_from(&key[T::key_size()..]) + .expect("Due to trait bounds, k2 should always be half the size of the XTS key"); + + Self::new_from_split_keys(k1, k2, iv) + } +} +impl Decryptor +where + BS: BlockSizes, + C: BlockCipherDecrypt + KeyInit + KeySizeUser, + T: BlockCipherEncrypt + KeyInit + KeySizeUser, +{ + /// Create an XTS array and precompute it + pub fn new_from_split_keys(k1: &Key, k2: &Key, iv: &Block) -> Self { let cipher = C::new(&k1); + let tweaker = T::new(&k2); + let iv = precompute_iv(&tweaker, &iv); + + Self { + cipher, + tweaker, + iv, + } + } - Self { cipher, iv } + /// Change the IV + pub fn reset_iv(&mut self, iv: &Block) { + self.iv = precompute_iv(&self.tweaker, iv) } } -impl BlockSizeUser for Decryptor +impl BlockSizeUser for Decryptor where - C: BlockCipherDecrypt, + BS: BlockSizes, + C: BlockCipherDecrypt, + T: BlockCipherEncrypt, { type BlockSize = C::BlockSize; } -impl BlockModeDecrypt for Decryptor +impl BlockModeDecrypt for Decryptor where - C: BlockCipherDecrypt, + BS: BlockSizes, + C: BlockCipherDecrypt, + T: BlockCipherEncrypt, { fn decrypt_with_backend(&mut self, f: impl BlockModeDecClosure) { struct Closure<'a, BS, BC> @@ -94,41 +130,30 @@ where } } - let Self { cipher, iv } = self; + // tweaker is only used when setting up IV + let Self { + cipher, + tweaker: _, + iv, + } = self; cipher.decrypt_with_backend(Closure { iv, f }) } } -impl InnerUser for Decryptor +impl IvSizeUser for Decryptor where - C: BlockCipherDecrypt, -{ - type Inner = C; -} - -impl IvSizeUser for Decryptor -where - C: BlockCipherDecrypt, + BS: BlockSizes, + C: BlockCipherDecrypt, + T: BlockCipherEncrypt, { type IvSize = C::BlockSize; } -impl InnerIvInit for Decryptor -where - C: BlockCipherDecrypt, -{ - #[inline] - fn inner_iv_init(cipher: C, iv: &Iv) -> Self { - Self { - cipher, - iv: iv.clone(), - } - } -} - -impl IvState for Decryptor +impl IvState for Decryptor where - C: BlockCipherDecrypt, + BS: BlockSizes, + C: BlockCipherDecrypt, + T: BlockCipherEncrypt, { #[inline] fn iv_state(&self) -> Iv { @@ -136,20 +161,26 @@ where } } -impl AlgorithmName for Decryptor +impl AlgorithmName for Decryptor where - C: BlockCipherDecrypt + AlgorithmName, + BS: BlockSizes, + C: BlockCipherDecrypt + AlgorithmName, + T: BlockCipherEncrypt + AlgorithmName, { fn write_alg_name(f: &mut fmt::Formatter<'_>) -> fmt::Result { f.write_str("xts::Decryptor<")?; ::write_alg_name(f)?; + f.write_str(",")?; + ::write_alg_name(f)?; f.write_str(">") } } -impl fmt::Debug for Decryptor +impl fmt::Debug for Decryptor where - C: BlockCipherDecrypt + AlgorithmName, + BS: BlockSizes, + C: BlockCipherDecrypt + AlgorithmName, + T: BlockCipherEncrypt + AlgorithmName, { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.write_str("xts::Decryptor<")?; @@ -158,7 +189,12 @@ where } } -impl Drop for Decryptor { +impl Drop for Decryptor +where + BS: BlockSizes, + C: BlockCipherDecrypt, + T: BlockCipherEncrypt, +{ fn drop(&mut self) { #[cfg(feature = "zeroize")] self.iv.zeroize(); diff --git a/xts/src/encrypt.rs b/xts/src/encrypt.rs index d2a8f20..5f59ded 100644 --- a/xts/src/encrypt.rs +++ b/xts/src/encrypt.rs @@ -1,12 +1,12 @@ use crate::xts_core::{precompute_iv, Xts}; use cipher::{ - crypto_common::{BlockSizes, InnerUser}, - AlgorithmName, Array, Block, BlockCipherEncBackend, BlockCipherEncClosure, BlockCipherEncrypt, - BlockModeEncBackend, BlockModeEncClosure, BlockModeEncrypt, BlockSizeUser, InOut, InnerIvInit, - Iv, IvSizeUser, IvState, KeyInit, ParBlocks, ParBlocksSizeUser, + array::ArraySize, consts::B1, crypto_common::BlockSizes, typenum::Double, AlgorithmName, Array, + Block, BlockCipherEncBackend, BlockCipherEncClosure, BlockCipherEncrypt, BlockModeEncBackend, + BlockModeEncClosure, BlockModeEncrypt, BlockSizeUser, InOut, Iv, IvSizeUser, IvState, Key, + KeyInit, KeyIvInit, KeySizeUser, ParBlocks, ParBlocksSizeUser, }; -use core::fmt; +use core::{fmt, ops::Shl}; #[cfg(feature = "zeroize")] use cipher::zeroize::{Zeroize, ZeroizeOnDrop}; @@ -18,30 +18,56 @@ where C: BlockCipherEncrypt, { cipher: C, + tweaker: C, iv: Block, } // This would probably be the cleanest way to do it, but it would require a way to multiply a typenum by 2 -// impl KeySizeUser for Encryptor where C: KeySizeUser { -// type KeySize = C::KeySize * 2; -// } +impl KeySizeUser for Encryptor +where + KS: ArraySize + Shl, + >::Output: ArraySize, + C: BlockCipherEncrypt + KeySizeUser, +{ + type KeySize = Double; +} + +impl KeyIvInit for Encryptor +where + C: BlockCipherEncrypt + KeySizeUser + KeyInit, + Encryptor: KeySizeUser, +{ + fn new(key: &Key, iv: &Iv) -> Self { + // Split the key and call split key constructor + let k1 = <&Key>::try_from(&key[..C::key_size()]) + .expect("Due to trait bounds, k1 should always be half the size of the XTS key"); + let k2 = <&Key>::try_from(&key[C::key_size()..]) + .expect("Due to trait bounds, k2 should always be half the size of the XTS key"); + + Self::new_from_split_keys(k1, k2, iv) + } +} impl Encryptor where - C: BlockCipherEncrypt + KeyInit, + C: BlockCipherEncrypt + KeyInit + KeySizeUser, { /// Create an XTS array and precompute it - pub fn new_xts( - k1: Array, - k2: Array, - mut iv: Block, - ) -> Self { + pub fn new_from_split_keys(k1: &Key, k2: &Key, iv: &Block) -> Self { + let cipher = C::new(&k1); let tweaker = C::new(&k2); - precompute_iv(&tweaker, &mut iv); + let iv = precompute_iv(&tweaker, &iv); - let cipher = C::new(&k1); + Self { + cipher, + tweaker, + iv, + } + } - Self { cipher, iv } + /// Change the IV + pub fn reset_iv(&mut self, iv: &Block) { + self.iv = precompute_iv(&self.tweaker, iv) } } @@ -52,14 +78,6 @@ where type BlockSize = C::BlockSize; } -// Note: Needs to be removed if we want to override key size -impl InnerUser for Encryptor -where - C: BlockCipherEncrypt, -{ - type Inner = C; -} - impl IvSizeUser for Encryptor where C: BlockCipherEncrypt, @@ -67,19 +85,6 @@ where type IvSize = C::BlockSize; } -impl InnerIvInit for Encryptor -where - C: BlockCipherEncrypt, -{ - #[inline] - fn inner_iv_init(cipher: C, iv: &Iv) -> Self { - Self { - cipher, - iv: iv.clone(), - } - } -} - impl IvState for Encryptor where C: BlockCipherEncrypt, @@ -124,7 +129,12 @@ where } } - let Self { cipher, iv } = self; + // tweaker is only used when setting up IV + let Self { + cipher, + tweaker: _, + iv, + } = self; let f = Closure { iv, f }; cipher.encrypt_with_backend(f) } diff --git a/xts/src/xts_core.rs b/xts/src/xts_core.rs index ec78e2f..ade5c22 100644 --- a/xts/src/xts_core.rs +++ b/xts/src/xts_core.rs @@ -1,6 +1,6 @@ use cipher::{ - crypto_common::BlockSizes, Array, Block, BlockCipherEncrypt, BlockSizeUser, InOut, InOutBuf, - ParBlocks, ParBlocksSizeUser, + crypto_common::BlockSizes, Array, Block, BlockCipherDecBackend, BlockCipherEncrypt, + BlockSizeUser, InOut, InOutBuf, ParBlocks, ParBlocksSizeUser, }; use crate::xor; @@ -9,12 +9,14 @@ use crate::xor; const GF_MOD: u8 = 0x87; /// Since the traits does not allow using two engines, this is used to pre-compute the IV. -pub fn precompute_iv(cipher: &BC, iv: &mut Array) +pub fn precompute_iv(cipher: &BC, iv: &Block) -> Block where BS: BlockSizes, BC: BlockCipherEncrypt, { - cipher.encrypt_block(iv); + let mut output = iv.clone(); + cipher.encrypt_block(&mut output); + output } fn gf_mul(tweak: &mut Array) -> bool From 6f789cb8f72924f2c0d6ce6f3b20aea8accd3ec0 Mon Sep 17 00:00:00 2001 From: zer0x64 Date: Fri, 22 Nov 2024 18:05:06 -0500 Subject: [PATCH 06/23] [XTS] Removed unnecessary generic in Decryptor --- xts/src/decrypt.rs | 34 ++++++++++++++++------------------ 1 file changed, 16 insertions(+), 18 deletions(-) diff --git a/xts/src/decrypt.rs b/xts/src/decrypt.rs index c0f3dc1..a5c287a 100644 --- a/xts/src/decrypt.rs +++ b/xts/src/decrypt.rs @@ -16,18 +16,17 @@ use cipher::zeroize::{Zeroize, ZeroizeOnDrop}; /// XTS mode decryptor. #[derive(Clone)] -pub struct Decryptor +pub struct Decryptor where - BS: BlockSizes, - C: BlockCipherDecrypt, - T: BlockCipherEncrypt, + C: BlockCipherDecrypt, + T: BlockCipherEncrypt, { cipher: C, tweaker: T, iv: Block, } -impl KeySizeUser for Decryptor +impl KeySizeUser for Decryptor where KS: ArraySize + Shl, >::Output: ArraySize, @@ -38,13 +37,13 @@ where type KeySize = Double; } -impl KeyIvInit for Decryptor +impl KeyIvInit for Decryptor where KS: ArraySize, BS: BlockSizes, C: BlockCipherDecrypt + KeySizeUser + KeyInit, T: BlockCipherEncrypt + KeySizeUser + KeyInit, - Decryptor: KeySizeUser, + Decryptor: KeySizeUser, { fn new(key: &Key, iv: &Iv) -> Self { // Split the key and call split key constructor @@ -57,7 +56,7 @@ where } } -impl Decryptor +impl Decryptor where BS: BlockSizes, C: BlockCipherDecrypt + KeyInit + KeySizeUser, @@ -82,7 +81,7 @@ where } } -impl BlockSizeUser for Decryptor +impl BlockSizeUser for Decryptor where BS: BlockSizes, C: BlockCipherDecrypt, @@ -91,7 +90,7 @@ where type BlockSize = C::BlockSize; } -impl BlockModeDecrypt for Decryptor +impl BlockModeDecrypt for Decryptor where BS: BlockSizes, C: BlockCipherDecrypt, @@ -140,7 +139,7 @@ where } } -impl IvSizeUser for Decryptor +impl IvSizeUser for Decryptor where BS: BlockSizes, C: BlockCipherDecrypt, @@ -149,7 +148,7 @@ where type IvSize = C::BlockSize; } -impl IvState for Decryptor +impl IvState for Decryptor where BS: BlockSizes, C: BlockCipherDecrypt, @@ -161,7 +160,7 @@ where } } -impl AlgorithmName for Decryptor +impl AlgorithmName for Decryptor where BS: BlockSizes, C: BlockCipherDecrypt + AlgorithmName, @@ -176,7 +175,7 @@ where } } -impl fmt::Debug for Decryptor +impl fmt::Debug for Decryptor where BS: BlockSizes, C: BlockCipherDecrypt + AlgorithmName, @@ -189,11 +188,10 @@ where } } -impl Drop for Decryptor +impl Drop for Decryptor where - BS: BlockSizes, - C: BlockCipherDecrypt, - T: BlockCipherEncrypt, + C: BlockCipherDecrypt, + T: BlockCipherEncrypt, { fn drop(&mut self) { #[cfg(feature = "zeroize")] From 15847834cae20746408ba4ef9fd9c77ae658b10a Mon Sep 17 00:00:00 2001 From: zer0x64 Date: Fri, 22 Nov 2024 18:32:18 -0500 Subject: [PATCH 07/23] [XTS] Added GF tests --- xts/src/gf.rs | 116 ++++++++++++++++++++++++++++++++++++++++++++ xts/src/lib.rs | 1 + xts/src/xts_core.rs | 68 ++------------------------ 3 files changed, 120 insertions(+), 65 deletions(-) create mode 100644 xts/src/gf.rs diff --git a/xts/src/gf.rs b/xts/src/gf.rs new file mode 100644 index 0000000..26f9cb9 --- /dev/null +++ b/xts/src/gf.rs @@ -0,0 +1,116 @@ +use cipher::{crypto_common::BlockSizes, Array}; + +/// Derived from the polynomial x^128 + x^5 + x + 1 +const GF_MOD: u8 = 0x87; + +pub fn gf_mul(tweak: &mut Array) -> bool +where + BS: BlockSizes, +{ + let mut carry = 0; + + for i in 0..BS::to_usize() { + // Save carry from previous byte + let old_carry = carry; + + // Check if there is a carry for this shift + carry = (tweak[i] & 0x80) >> 7; + + // Shift left + tweak[i] <<= 1; + + // Carry over bit from last carry + tweak[i] |= old_carry; + } + + // If there is a carry, we mod by the polynomial + if carry == 1 { + tweak[0] ^= GF_MOD; + } + + carry == 1 +} + +// This is only used once when decrypting with ciphertext stealing +pub fn gf_reverse_mul(tweak: &mut Array, carry: bool) +where + BS: BlockSizes, +{ + if carry { + tweak[0] ^= GF_MOD; + } + + let mut new_carry = 0; + + for i in (0..BS::to_usize()).rev() { + // Save carry from previous byte + let old_carry = new_carry; + + // Check if there is a carry for this shift + new_carry = tweak[i] & 1; + + // Shift right + tweak[i] >>= 1; + + // Carry over bit from last carry + tweak[i] |= old_carry << 7; + } + + // If there is a carry, we mod by the polynomial + if carry { + let len = tweak.len(); + tweak[len - 1] |= 0x80; + } +} + +#[cfg(test)] +mod tests { + use cipher::{consts::U16, Array}; + + use crate::gf::{gf_mul, gf_reverse_mul}; + + #[test] + fn test_gf_mul() { + let mut input = Array::::try_from([0x55; 16]).unwrap(); + let expected_output = [0xAA; 16]; + + let carry = gf_mul(&mut input); + + assert!(!carry); + assert_eq!(input, expected_output); + } + + #[test] + fn test_gf_mul_overflow() { + let mut input = Array::::try_from([0xAA; 16]).unwrap(); + let mut expected_output = [0x55; 16]; + expected_output[0] = 0xd3; + + let carry = gf_mul(&mut input); + + assert!(carry); + assert_eq!(input, expected_output) + } + + #[test] + fn test_gf_reverse_mul() { + let mut input = Array::::try_from([0xAA; 16]).unwrap(); + let expected_output = [0x55; 16]; + + gf_reverse_mul(&mut input, false); + + assert_eq!(input, expected_output); + } + + #[test] + fn test_gf_reverse_mul_overflow() { + let mut input = Array::::try_from([0xAA; 16]).unwrap(); + let mut expected_output = [0x55; 16]; + expected_output[0] = 0x16; + expected_output[15] = 0xd5; + + gf_reverse_mul(&mut input, true); + + assert_eq!(input, expected_output); + } +} diff --git a/xts/src/lib.rs b/xts/src/lib.rs index ee5f4fc..c532a3c 100644 --- a/xts/src/lib.rs +++ b/xts/src/lib.rs @@ -95,6 +95,7 @@ use cipher::array::{Array, ArraySize}; mod decrypt; mod encrypt; +mod gf; mod xts_core; pub use decrypt::Decryptor; diff --git a/xts/src/xts_core.rs b/xts/src/xts_core.rs index ade5c22..acc874b 100644 --- a/xts/src/xts_core.rs +++ b/xts/src/xts_core.rs @@ -1,13 +1,11 @@ use cipher::{ - crypto_common::BlockSizes, Array, Block, BlockCipherDecBackend, BlockCipherEncrypt, - BlockSizeUser, InOut, InOutBuf, ParBlocks, ParBlocksSizeUser, + crypto_common::BlockSizes, Array, Block, BlockCipherEncrypt, BlockSizeUser, InOut, InOutBuf, + ParBlocks, ParBlocksSizeUser, }; +use crate::gf::{gf_mul, gf_reverse_mul}; use crate::xor; -/// Derived from the polynomial x^128 + x^5 + x + 1 -const GF_MOD: u8 = 0x87; - /// Since the traits does not allow using two engines, this is used to pre-compute the IV. pub fn precompute_iv(cipher: &BC, iv: &Block) -> Block where @@ -19,66 +17,6 @@ where output } -fn gf_mul(tweak: &mut Array) -> bool -where - BS: BlockSizes, -{ - let mut carry = 0; - - for i in 0..BS::to_usize() { - // Save carry from previous byte - let old_carry = carry; - - // Check if there is a carry for this shift - carry = (tweak[i] & 0x80) >> 7; - - // Shift left - tweak[i] <<= 1; - - // Carry over bit from last carry - tweak[i] |= old_carry; - } - - // If there is a carry, we mod by the polynomial - if carry == 1 { - tweak[0] ^= GF_MOD; - } - - carry == 1 -} - -// This is only used once when decrypting with ciphertext stealing -fn gf_reverse_mul(tweak: &mut Array, carry: bool) -where - BS: BlockSizes, -{ - if carry { - tweak[0] ^= GF_MOD; - } - - let mut new_carry = 0; - - for i in BS::to_usize() - 1..=0 { - // Save carry from previous byte - let old_carry = new_carry; - - // Check if there is a carry for this shift - new_carry = tweak[i] & 1; - - // Shift right - tweak[i] >>= 1; - - // Carry over bit from last carry - tweak[i] |= old_carry << 7; - } - - // If there is a carry, we mod by the polynomial - if carry { - let len = tweak.len(); - tweak[len - 1] |= 0x80; - } -} - /// Core implementation of XTS mode pub trait Xts: ParBlocksSizeUser + BlockSizeUser { /// Method to encrypt/decrypt a single block without mode. From 43a1603d5f916bb43b2ba79672e762c5a2cdaa71 Mon Sep 17 00:00:00 2001 From: zer0x64 Date: Sat, 23 Nov 2024 19:53:05 -0500 Subject: [PATCH 08/23] [XTS] Added IEEE tests --- xts/src/decrypt.rs | 13 +++- xts/src/encrypt.rs | 7 +- xts/src/gf.rs | 6 +- xts/src/lib.rs | 19 +++++ xts/src/xts_core.rs | 135 ++++++++++++++++++++++------------ xts/tests/aes.rs | 43 +++++++++++ xts/tests/data/ieee_vec1.blb | Bin 0 -> 86 bytes xts/tests/data/ieee_vec10.blb | Bin 0 -> 1112 bytes xts/tests/data/ieee_vec11.blb | Bin 0 -> 1112 bytes xts/tests/data/ieee_vec12.blb | Bin 0 -> 1112 bytes xts/tests/data/ieee_vec13.blb | Bin 0 -> 1112 bytes xts/tests/data/ieee_vec14.blb | Bin 0 -> 1112 bytes xts/tests/data/ieee_vec15.blb | Bin 0 -> 87 bytes xts/tests/data/ieee_vec16.blb | Bin 0 -> 89 bytes xts/tests/data/ieee_vec17.blb | Bin 0 -> 91 bytes xts/tests/data/ieee_vec18.blb | Bin 0 -> 93 bytes xts/tests/data/ieee_vec19.blb | Bin 0 -> 1079 bytes xts/tests/data/ieee_vec2.blb | Bin 0 -> 117 bytes xts/tests/data/ieee_vec3.blb | Bin 0 -> 117 bytes xts/tests/data/ieee_vec4.blb | Bin 0 -> 1079 bytes xts/tests/data/ieee_vec5.blb | Bin 0 -> 1079 bytes xts/tests/data/ieee_vec6.blb | Bin 0 -> 1079 bytes xts/tests/data/ieee_vec7.blb | Bin 0 -> 1079 bytes xts/tests/data/ieee_vec8.blb | Bin 0 -> 1079 bytes xts/tests/data/ieee_vec9.blb | Bin 0 -> 1079 bytes 25 files changed, 166 insertions(+), 57 deletions(-) create mode 100644 xts/tests/aes.rs create mode 100644 xts/tests/data/ieee_vec1.blb create mode 100644 xts/tests/data/ieee_vec10.blb create mode 100644 xts/tests/data/ieee_vec11.blb create mode 100644 xts/tests/data/ieee_vec12.blb create mode 100644 xts/tests/data/ieee_vec13.blb create mode 100644 xts/tests/data/ieee_vec14.blb create mode 100644 xts/tests/data/ieee_vec15.blb create mode 100644 xts/tests/data/ieee_vec16.blb create mode 100644 xts/tests/data/ieee_vec17.blb create mode 100644 xts/tests/data/ieee_vec18.blb create mode 100644 xts/tests/data/ieee_vec19.blb create mode 100644 xts/tests/data/ieee_vec2.blb create mode 100644 xts/tests/data/ieee_vec3.blb create mode 100644 xts/tests/data/ieee_vec4.blb create mode 100644 xts/tests/data/ieee_vec5.blb create mode 100644 xts/tests/data/ieee_vec6.blb create mode 100644 xts/tests/data/ieee_vec7.blb create mode 100644 xts/tests/data/ieee_vec8.blb create mode 100644 xts/tests/data/ieee_vec9.blb diff --git a/xts/src/decrypt.rs b/xts/src/decrypt.rs index a5c287a..6b7e3fb 100644 --- a/xts/src/decrypt.rs +++ b/xts/src/decrypt.rs @@ -1,6 +1,6 @@ use crate::xts_core::{precompute_iv, Xts}; use cipher::{ - array::{Array, ArraySize}, + array::ArraySize, consts::B1, crypto_common::{BlockSizes, IvSizeUser}, inout::InOut, @@ -102,7 +102,7 @@ where BS: BlockSizes, BC: BlockModeDecClosure, { - iv: &'a mut Array, + iv: &'a mut Block, f: BC, } @@ -200,14 +200,19 @@ where } #[cfg(feature = "zeroize")] -impl ZeroizeOnDrop for Decryptor {} +impl ZeroizeOnDrop for Decryptor +where + C: BlockCipherDecrypt + ZeroizeOnDrop, + T: BlockCipherEncrypt + ZeroizeOnDrop, +{ +} struct Backend<'a, BS, BK> where BS: BlockSizes, BK: BlockCipherDecBackend, { - iv: &'a mut Array, + iv: &'a mut Block, cipher_backend: &'a BK, } diff --git a/xts/src/encrypt.rs b/xts/src/encrypt.rs index 5f59ded..4d8a6fc 100644 --- a/xts/src/encrypt.rs +++ b/xts/src/encrypt.rs @@ -1,7 +1,7 @@ use crate::xts_core::{precompute_iv, Xts}; use cipher::{ - array::ArraySize, consts::B1, crypto_common::BlockSizes, typenum::Double, AlgorithmName, Array, + array::ArraySize, consts::B1, crypto_common::BlockSizes, typenum::Double, AlgorithmName, Block, BlockCipherEncBackend, BlockCipherEncClosure, BlockCipherEncrypt, BlockModeEncBackend, BlockModeEncClosure, BlockModeEncrypt, BlockSizeUser, InOut, Iv, IvSizeUser, IvState, Key, KeyInit, KeyIvInit, KeySizeUser, ParBlocks, ParBlocksSizeUser, @@ -105,7 +105,7 @@ where BS: BlockSizes, BM: BlockModeEncClosure, { - iv: &'a mut Array, + iv: &'a mut Block, f: BM, } @@ -135,6 +135,7 @@ where tweaker: _, iv, } = self; + let f = Closure { iv, f }; cipher.encrypt_with_backend(f) } @@ -177,7 +178,7 @@ where BS: BlockSizes, BC: BlockCipherEncBackend, { - iv: &'a mut Array, + iv: &'a mut Block, cipher_backend: &'a BC, } diff --git a/xts/src/gf.rs b/xts/src/gf.rs index 26f9cb9..c2caa50 100644 --- a/xts/src/gf.rs +++ b/xts/src/gf.rs @@ -68,7 +68,7 @@ mod tests { use cipher::{consts::U16, Array}; use crate::gf::{gf_mul, gf_reverse_mul}; - + #[test] fn test_gf_mul() { let mut input = Array::::try_from([0x55; 16]).unwrap(); @@ -98,7 +98,7 @@ mod tests { let expected_output = [0x55; 16]; gf_reverse_mul(&mut input, false); - + assert_eq!(input, expected_output); } @@ -110,7 +110,7 @@ mod tests { expected_output[15] = 0xd5; gf_reverse_mul(&mut input, true); - + assert_eq!(input, expected_output); } } diff --git a/xts/src/lib.rs b/xts/src/lib.rs index c532a3c..fbea506 100644 --- a/xts/src/lib.rs +++ b/xts/src/lib.rs @@ -101,6 +101,25 @@ mod xts_core; pub use decrypt::Decryptor; pub use encrypt::Encryptor; +/// Error which indicates that message is smaller than cipher's block size. +#[derive(Copy, Clone, Debug)] +pub struct Error; + +/// Result type of the crate +pub type Result = core::result::Result; + +/// Encryption functionality of XTS modes. +pub trait Encrypt: Sized { + /// Encrypt buffer in place + fn encrypt(self, buf: &mut [u8]) -> Result<()>; +} + +/// Decryption functionality of XTS modes. +pub trait Decypt: Sized { + /// Decrypt buffer in place + fn decrypt(self, buf: &mut [u8]) -> Result<()>; +} + #[inline(always)] fn xor(out: &mut Array, buf: &Array) { for (a, b) in out.iter_mut().zip(buf) { diff --git a/xts/src/xts_core.rs b/xts/src/xts_core.rs index acc874b..ce55c5c 100644 --- a/xts/src/xts_core.rs +++ b/xts/src/xts_core.rs @@ -1,8 +1,11 @@ +use cipher::typenum::Unsigned; use cipher::{ crypto_common::BlockSizes, Array, Block, BlockCipherEncrypt, BlockSizeUser, InOut, InOutBuf, ParBlocks, ParBlocksSizeUser, }; +use crate::{Error, Result}; + use crate::gf::{gf_mul, gf_reverse_mul}; use crate::xor; @@ -31,18 +34,18 @@ pub trait Xts: ParBlocksSizeUser + BlockSizeUser { /// There is a slight difference regarding the tweak during decryption fn is_decrypt() -> bool; - // Unused but keeping for now just in case - // fn process(&self, mut block: InOut<'_, '_, Block>) { - // let mut b = block.clone_in(); - // self.process_inplace(&mut b); + //Unused but keeping for now just in case + fn _process(&self, mut block: InOut<'_, '_, Block>) { + let mut b = block.clone_in(); + self.process_inplace(&mut b); - // *block.get_out() = b; - // } + *block.get_out() = b; + } - // fn process_par(&self, blocks: InOut<'_, '_, ParBlocks>) { - // let mut blocks = blocks.clone_in(); - // self.process_par_inplace(&mut blocks); - // } + fn _process_par(&self, blocks: InOut<'_, '_, ParBlocks>) { + let mut blocks = blocks.clone_in(); + self.process_par_inplace(&mut blocks); + } fn process_block_inplace(&mut self, block: &mut Block) { { @@ -117,57 +120,95 @@ pub trait Xts: ParBlocksSizeUser + BlockSizeUser { *block.get_out() = b; } } +} - fn ciphertext_stealing(&mut self, buffer: &mut [u8]) { - let remaining_bytes = buffer.len() - Self::block_size(); - - let need_stealing = remaining_bytes > 0; - - { - let second_to_last_block: &mut Block = (&mut buffer[0..Self::block_size()]) - .try_into() - .expect("Not a full block on second to last block!"); +pub trait XtsMode: Xts { + fn process_all_in_place(&mut self, buffer: &mut [u8]) -> Result<()> { + let block_size = Self::block_size(); + let par_blocks_size = Self::ParBlocksSize::USIZE; - // This is a special case - if Self::is_decrypt() && need_stealing { - // We fast forward the multiplication here + if buffer.len() < block_size { + return Err(Error); + } - let carry = { - let mut iv = self.get_iv_mut(); - let carry = gf_mul(&mut iv); + let need_stealing = buffer.len() % Self::block_size() == 0; - xor(second_to_last_block, iv); + let (buffer, remaining_buffer) = if need_stealing { + buffer.split_at_mut((buffer.len() / block_size - 1) * block_size) + } else { + (buffer, [0u8; 0].as_mut_slice()) + }; - carry - }; + // Split buffer into blocks + let mut blocks = buffer + .chunks_exact_mut(block_size) + .map(|b| <&mut Block>::try_from(b).unwrap()); - self.process_inplace(second_to_last_block); + // Can't figure out how to get parblocks in place here, commenting for now + // if par_blocks_size > 1 { + // let mut blocks: alloc::vec::Vec<&mut Block> = blocks.collect(); - { - let iv = self.get_iv_mut(); - xor(second_to_last_block, iv); + // let mut par_blocks = blocks.chunks_exact_mut(par_blocks_size); + // for b in par_blocks { + // let mut b = <&mut ParBlocks>::try_from(*b).unwrap(); + // } - gf_reverse_mul(iv, carry); - } - } else { - // Process normally - self.process_block_inplace(second_to_last_block); - } + // self.process_tail_blocks_inplace(tail); + // } else { + for b in blocks { + self.process_block_inplace(b); } + //} if need_stealing { - // We first swap the remaining bytes with the previous block's + self.ciphertext_stealing(remaining_buffer); + }; + + Ok(()) + } + + fn ciphertext_stealing(&mut self, buffer: &mut [u8]) { + let remaining_bytes = buffer.len() - Self::block_size(); + + let second_to_last_block: &mut Block = (&mut buffer[0..Self::block_size()]) + .try_into() + .expect("Not a full block on second to last block!"); + + if Self::is_decrypt() { + // We fast forward the multiplication here + let carry = { + let mut iv = self.get_iv_mut(); + let carry = gf_mul(&mut iv); + + xor(second_to_last_block, iv); + + carry + }; + + self.process_inplace(second_to_last_block); + { - let (previous_block, current_block) = buffer.split_at_mut(Self::block_size()); - previous_block[..remaining_bytes] - .swap_with_slice(&mut current_block[..remaining_bytes]); - } + let iv = self.get_iv_mut(); + xor(second_to_last_block, iv); - // We can then decrypt the final block, which is not located at the second to last block - let second_to_last_block: &mut Block = (&mut buffer[0..Self::block_size()]) - .try_into() - .expect("Not a full block on second to last block!"); + gf_reverse_mul(iv, carry); + } + } else { + // Process normally self.process_block_inplace(second_to_last_block); } + + // We first swap the remaining bytes with the previous block's + { + let (previous_block, current_block) = buffer.split_at_mut(Self::block_size()); + previous_block[..remaining_bytes] + .swap_with_slice(&mut current_block[..remaining_bytes]); + } + + // We can then decrypt the final block, which is not located at the second to last block + let second_to_last_block: &mut Block = (&mut buffer[0..Self::block_size()]) + .try_into() + .expect("Not a full block on second to last block!"); + self.process_block_inplace(second_to_last_block); } } diff --git a/xts/tests/aes.rs b/xts/tests/aes.rs new file mode 100644 index 0000000..081c75b --- /dev/null +++ b/xts/tests/aes.rs @@ -0,0 +1,43 @@ +use aes::*; +use xts::{Decryptor, Encryptor}; +use cipher::{block_mode_dec_test, block_mode_enc_test}; + +// Test vectors from IEEE 1619-2018 +block_mode_enc_test!(aes128_xts_enc_vec1_test, "ieee_vec1", Encryptor); +block_mode_dec_test!(aes128_xts_dec_vec1_test, "ieee_vec1", Decryptor); +block_mode_enc_test!(aes128_xts_enc_vec2_test, "ieee_vec2", Encryptor); +block_mode_dec_test!(aes128_xts_dec_vec2_test, "ieee_vec2", Decryptor); +block_mode_enc_test!(aes128_xts_enc_vec3_test, "ieee_vec3", Encryptor); +block_mode_dec_test!(aes128_xts_dec_vec3_test, "ieee_vec3", Decryptor); +block_mode_enc_test!(aes128_xts_enc_vec4_test, "ieee_vec4", Encryptor); +block_mode_dec_test!(aes128_xts_dec_vec4_test, "ieee_vec4", Decryptor); +block_mode_enc_test!(aes128_xts_enc_vec5_test, "ieee_vec5", Encryptor); +block_mode_dec_test!(aes128_xts_dec_vec5_test, "ieee_vec5", Decryptor); +block_mode_enc_test!(aes128_xts_enc_vec6_test, "ieee_vec6", Encryptor); +block_mode_dec_test!(aes128_xts_dec_vec6_test, "ieee_vec6", Decryptor); +block_mode_enc_test!(aes128_xts_enc_vec7_test, "ieee_vec7", Encryptor); +block_mode_dec_test!(aes128_xts_dec_vec7_test, "ieee_vec7", Decryptor); +block_mode_enc_test!(aes128_xts_enc_vec8_test, "ieee_vec8", Encryptor); +block_mode_dec_test!(aes128_xts_dec_vec8_test, "ieee_vec8", Decryptor); +block_mode_enc_test!(aes128_xts_enc_vec9_test, "ieee_vec9", Encryptor); +block_mode_dec_test!(aes128_xts_dec_vec9_test, "ieee_vec9", Decryptor); +block_mode_enc_test!(aes256_xts_enc_vec10_test, "ieee_vec10", Encryptor); +block_mode_dec_test!(aes256_xts_dec_vec10_test, "ieee_vec10", Decryptor); +block_mode_enc_test!(aes256_xts_enc_vec11_test, "ieee_vec11", Encryptor); +block_mode_dec_test!(aes256_xts_dec_vec11_test, "ieee_vec11", Decryptor); +block_mode_enc_test!(aes256_xts_enc_vec12_test, "ieee_vec12", Encryptor); +block_mode_dec_test!(aes256_xts_dec_vec12_test, "ieee_vec12", Decryptor); +block_mode_enc_test!(aes256_xts_enc_vec13_test, "ieee_vec13", Encryptor); +block_mode_dec_test!(aes256_xts_dec_vec13_test, "ieee_vec13", Decryptor); +block_mode_enc_test!(aes256_xts_enc_vec14_test, "ieee_vec14", Encryptor); +block_mode_dec_test!(aes256_xts_dec_vec14_test, "ieee_vec14", Decryptor); +block_mode_enc_test!(aes128_xts_enc_vec15_test, "ieee_vec15", Encryptor); +block_mode_dec_test!(aes128_xts_dec_vec15_test, "ieee_vec15", Decryptor); +block_mode_enc_test!(aes128_xts_enc_vec16_test, "ieee_vec16", Encryptor); +block_mode_dec_test!(aes128_xts_dec_vec16_test, "ieee_vec16", Decryptor); +block_mode_enc_test!(aes128_xts_enc_vec17_test, "ieee_vec17", Encryptor); +block_mode_dec_test!(aes128_xts_dec_vec17_test, "ieee_vec17", Decryptor); +block_mode_enc_test!(aes128_xts_enc_vec18_test, "ieee_vec18", Encryptor); +block_mode_dec_test!(aes128_xts_dec_vec18_test, "ieee_vec18", Decryptor); +block_mode_enc_test!(aes128_xts_enc_vec19_test, "ieee_vec19", Encryptor); +block_mode_dec_test!(aes128_xts_dec_vec19_test, "ieee_vec19", Decryptor); diff --git a/xts/tests/data/ieee_vec1.blb b/xts/tests/data/ieee_vec1.blb new file mode 100644 index 0000000000000000000000000000000000000000..076cc9175b03914e89fbcbb8cf649cda3a6757ba GIT binary patch literal 86 zcmZQnU?2b(F;p-*Osx4fZ*Rt?H?!xzT>R?pvPoy1FMXXl<85mX)1g%T{gd64XPl2VMD z)u%FbI8GOvJkvaowTZK2it8kW{|u<0oq>UoiJ66!jh%y&i<^g+k6%DgNLWNvOk6@z zN?Jx%PF_J#Nm)fzObje8tLDtJkbuw|>LM zO`ErD-L`$l&Rx6r?A^Ejz`;X@j~qRA{KUyqr_Y={cmBe~OP8-)y>|V^&0Dwc+`V`I z!NW(7pFDl`{Kd;xuiw0V_x{7jPoKYh{r3IG&tJd){QdWTr2D^}LB`rjK);;1?MZXa z%*O@iXTM@-u#fXl4qviP=!(eVXV$TXkIH`h^Xg68EdFy%hP_J$SKAuXwSSCV_x(HV zy|LqOc-f>J&&8Z^8#(?8%-Hp&VAZnBnaj1BKGn8#D_&XdxZ&to-}wJ?*L1GiC8U{n z!K$=S`DHtQ*LeX3CXV(_5zSLtc2%{t-#0Rcip6x#%H^tku_5V50Pm5rLOlE>61N^- z*jgcD(qO+lMDh59`w#C)n0#uuo>pyCpVM3$7dgPK$5b#pE`NiL2#d zUw@0LLcu}1`SKdGpZf3d5D72uPfQSfXjH|t(cxo2+-6Rr+zj*n=90*mmbooo4jaF5 zQSJzIKJC7@@aWrhv!Bh<|95WVzLWP<{P&5@)E3-c!@@7Lw?Auol*@vjJen(4AHH&9 z@}#1cqwOJQ9!&|F-zIYB((08HC&j-#vaaO+nml9c$*;ZsvsB)i<;dp}@ZfIMlVgu2 zax#R+E@Aw`&N@FOuDfpb-}lB-3_||5Fp5k*RBfX9>g|@d!DTK|uGKQTeMAHHP5-{yAx+Z8XH~iFS_?++OPu=4LrRIsfm64XPl2VMD z)u%FbI8GOvJkvaowTZK2it8kW|Nj|KKsy5iBNHqPshPQjrIodft)0Dtqm#3XtDC!rrn zub+QFU{G*KXjpheWK?uaY+QUoVp4KSYFc_mW>$7iZeD&tVNr2OX<2ziWmR=eZC!ms zV^ecWYg>CqXIFPmZ(sj}iIXNznL2IyjG41$&zU=K{(^;z7B5-4Z25|nt5&aByKeo4 zjhi-a*}84}j-9)9@7cR=|AB*t4j(ys?D&b3r%snAuU@}-`|kaRkDoq&`TFhqkDtGO|M~mx|48?LJ45+mp}@p0(Puk# zWz+8es}9)8y*5;tbr+NPGJ%Y~|8;^7FDQjrJeaJao4cvx?+eB&ryk@hOggTy$-Oo5 ze#8s&LvHh)EcsRG=e?)iat~9U&2}B*4ITZeTh@Hd(wf2Y@m#KLp6szx60+VQiIrDF zT~?JR9N}dv5w$%2`0QN$w17>F_7~pmIJD#OjY|$^*6I4pFybn4dU)b`l+(3m=a(FM zv#_#Pw5RIL{U%R-mEE%~9&+rtC3N`L&qacchZC(M%<3m;tx`Ly^vSlMcU|VJ>!%%7 ztmV#AZ<%g;Iw7p(KzDEEjAOIbu*_6!m)o6cAmt``s^QI+*2S_VPRi$c?dk6! z=i$_kULt(hynh+1Q|Pgu_g$v8Y40{_&yb%iol(=#wZFypPyeB2!>5&#dM;{TvgCTG ze4e?nSLEK^rw0!CbZwB>f2#BpXTEN*ysenz^VW}Rmzba0sW*c)V)LDc?q0u3_rKlW z|J7$xVjq*s$u+z-X}<#_doJA;{JGDvQfi+|O){r{jjMJ2kD9H!&9)z(_nV{WVnM_X zYthvlX=}S8Qgfo}6mH))=vGO*S;KdL$xB_7HOTY4wR`VfcZYka{-vif@_36~kIlQv zb4RP&SX;hBMWE}X`D<3z)`Y(2CY<#LCWQR{-}!Fct4evVs*OL}nx-uI`Ka1yeIvu^ Mxsm67rbI9U0C7PO&j0`b literal 0 HcmV?d00001 diff --git a/xts/tests/data/ieee_vec12.blb b/xts/tests/data/ieee_vec12.blb new file mode 100644 index 0000000000000000000000000000000000000000..c8bb8d140ff65b65d126aa72253686a0873ae3f6 GIT binary patch literal 1112 zcmZQ*U{IIPkkD|Q;Hn&)pixq2s+Qzg964XPl2VMD z)u%FbI8GOvJkvaowTZK2it8kW|Ns9pAcJ-W21X`k7FITP4o)s^9$r3v0YM>Q5m7O5 z2}vnw8Cf}b1w|!g6;(BL4NWa=9bG+r14AQY6H_yD3rj0&8(TYj2S+Do7gslT4^J;| zA74NJfWV;OkkGL3h{&kunAo`Zgv6xel+?8JjLfX;oZP(pg2JNWlG3vBipr|$n%cVh zhQ_Amme#iRj?S*`p5DIx2@@wxo-%dX^cgc}&7L!N-uwj%7cE}0blLJ1D_5;vvv%G3 z4I4LY-m-Pu_8mKS?cTF@-~Iy!4;?;o^w{weCr_O|bN1Z%3l}e4zH;^2^&2;D-M(}8 z-u(v;A3c8Z^x5+lFJHZW^Y-2Q4a!-~W;B|8|DQ?W>k{FKJ%C z{Kadx=@o``=Y75wubS((M)}j(pCV5lF|61!;fdZew+7u~@3h^e)f=Z?n_GXd4DD=I`ST&^ zy=h>Ry{t>9E3_ciF|xH;|K6%+Cmv1|c;PE8FF8wXlHj~M(}dL`KFnQyO!@t}<3hnz zwo?x5kF7J+JL&bbvi`Pt?ZHsNfFh~;qI=a=yg&43)zyr~U-OdObTmG5Jv_H&rHx6< zC+F?uXP``|lr2-}uX5q5i>d(Tg-$nMEz9 zO9_1XoMR-tRoL+C`_1z#Q;xr9pE>30w%K}jo0rRd^_(vE(%$9O;mQ4HugNn0Dy*El zcm=17kH@Ncmy-Xpd)}XV#s7U4_tuIxnXXw&9l9RtRDI~WzN=GFqt1HYW9hQ$2U&at zF>PLD6V2~yXeu?oYFy;OI%qSMK6SAJ#co~gWl9?TY2N?!Qx QtM0Xq9)tN&wS^jL07L*5`~Uy| literal 0 HcmV?d00001 diff --git a/xts/tests/data/ieee_vec13.blb b/xts/tests/data/ieee_vec13.blb new file mode 100644 index 0000000000000000000000000000000000000000..d2ddca8276855d9bdd91d360955890ebc067f139 GIT binary patch literal 1112 zcmZQ*U{IIPkkD|Q;Hn&)pixq2s+Qzg964XPl2VMD z)u%FbI8GOvJkvaowTZK2it8kW|3JWi1lk!G7@3$^SlQS)IJvlac=`AR1cih}M8(7< zB&DQfWaZ=)6qS@!RMpfqG_|yKboKNN42_IUOwG(KEUm0Lc_u%BBP>XV&mcy5|ffsQq$5iGPAOCa`W;F3X6(MO3TVCDyyn%YU}D7 z8k?G1THD$?I=i}idi(k(Oq?`%%G7DoXUv>6d(PZ>^A{{!w0OzVWy@ErT(x@5+I8zU zY}~YY%hqk%ckJA?d(Yl|`wtvEboj{8W5-XNJazia*>mSFT)cGo%GGPvZ`{0f`_A2a z_a8ib^!UlsXU|`}eD(Ux+js9jeEjtJ%hzw;fBgLQ`_JEh|3|w2+Zpx;U%GX#;)T+Z zo1QE8q^E6=W?S*T;8MEBvE_fvZg4$08pL1YwP8)Gi$Hf!bDJIiz$^Udcae3dpQ~vJLnU*g%{os6eg;G z?yBN^z14fUmLB+$aIyJ=3(JCQUou1c8h>r_602lg?Nd<^pmq6i|Ltd5j-fek%#Fe$ zgKb`XiZ`oi-yK?UEq&eOpO5~|+_NWMfA@->Z-I%HPW64XPl2VMD z)u%FbI8GOvJkvaowTZK2it8kW{~*AC0PPG6j7-cdtZeKYoLt;IynOrufkx|hxv2pPUiAl*RscGpMnOWI6xq0~og+;|BrDf$6l~vU>wRQCk zjZMuht!?ccon75My?y-?CQh0>W$Lu)GiJ`3J!kH``3n{J9h5cy=U*f{Ra*nI(+2lvEwIBo;rQz?78z7E?&BP<+v*#~fzIy%U?Ys9MK7RWADdLtVQf{?o5;b-kIa!;pT>INYd#0=XKGh}C-~Me`#r;0+lJ|F;GR+F6kfwu% zqE8s3*38rk6JEUa(6`nd?o&_a%~lJ$aiTH&sgBdniW|v3&KL4uP3qTBdXl;-we017 z$H^@LIfu{LgshRuPrsA)c{Ah0XEQ9gPtM#Bwddj71IO+hy2~JU)O44`y-oL}1O$`? znkA0Mo@^6bF!x9o*V#Ae`K#|M>9apj`w+RUXlq`~o<3Q{-8a(jPAU>&b!)uNx7=LV z=l;3L+kR+0Qc+g-i&)Skb2LCtmupKz+?0EnrTgNz{r)_ax%`|*b>Z*b*ElZ~uZ`x7 NOqL0j&*BQ-2>|4L3zq-@ literal 0 HcmV?d00001 diff --git a/xts/tests/data/ieee_vec15.blb b/xts/tests/data/ieee_vec15.blb new file mode 100644 index 0000000000000000000000000000000000000000..98b9064aa24a408677e7211003475466442587ec GIT binary patch literal 87 zcmZQ@`2X+kpWnZJ{`mgw>zB`;K7QE0Z||PnyLRr_zHRH4&6_rEP?%K_W+KFZ07?vu mOw25-Z0sDIT--doeEb4RIby1}-3o(rZA)h+$UZ;C`4#{%lqB*1 literal 0 HcmV?d00001 diff --git a/xts/tests/data/ieee_vec16.blb b/xts/tests/data/ieee_vec16.blb new file mode 100644 index 0000000000000000000000000000000000000000..aa1ad762486f304c72acf7fa32d18c5ef2a0a645 GIT binary patch literal 89 zcmZQ@`2X+kpWnZJ{`mgw>zB`;K7QE0Z||PnyLRr_zHRH4&6_rEP?%K_W+KFZ04fZO oOw25-Z0sDIT--doeEb4}Di<zB`;K7QE0Z||PnyLRr_zHRH4&6_rEP?%K_W+KFZ0BQ`3 rOw25-Z0sDIT--doeEb4}LTXR%3kMzu@m^@kZJd0z@5{co`{x1x(;FyC literal 0 HcmV?d00001 diff --git a/xts/tests/data/ieee_vec18.blb b/xts/tests/data/ieee_vec18.blb new file mode 100644 index 0000000000000000000000000000000000000000..265504a88d2c5ac5fdf2ea8405ff1a5d454f8428 GIT binary patch literal 93 zcmZQ@`2X+kpWnZJ{`mgw>zB`;K7QE0Z||PnyLRr_zHRH4&6_rEP?%K_W+KFZ02&O8 tOw25-Z0sDIT--doeEb4}Lc$tzTTTdnmt3V&{rFpA;qjw~-tM2f1^_8cDp&vj literal 0 HcmV?d00001 diff --git a/xts/tests/data/ieee_vec19.blb b/xts/tests/data/ieee_vec19.blb new file mode 100644 index 0000000000000000000000000000000000000000..dbcd357f7a78eec3b72f3d8143d6b9085120380c GIT binary patch literal 1079 zcmZQ@c<}Ji<0ns_J%91?)$2EJ-@QL@@X+BSM~@vpaq`sZGiT48S5S0LZC}ZN0PPG6 zj7-cdtZeKYoLt;IynOrufkx|hxv2pPUiAl*RscGpM znOWI6xq0~og+;|BrDf$6l~vU>wRQCkjZMuht!?ccon75My?y-?CQh0>W$Lu)GiJ`3 zJ!kH``3n{J9h5cy=U*f{peo2aPiXRD_5^w zzj5={?K^kx-A50S4a!-~W;B|8@q8EfGTRofkyk41el6p-#zq zs@IlPA36-IZVJ4*FS14ZS+`1}$lRXp74b8*Ul#01l9y%+w@-eNzCWUL&H4p?PG|O3 zUp}qjGjOWj7VXb3aSnfEJrD^6=v8{dx%cIZ+Z%DP-YwiXS^u!{>3NT5n(ccv z>t_B!{w;#q6U0sKu65WWQ86>VPjKDyi;j!FUg@s#ldI>}VQJ>eT=G}A!cj(Fqwc=P zu_I3pxi6g7@cp0Tx!+f2M<}vAPR!Z;^VslP7+D@Y_>K)mt`KSMu1EtLB>* zk{qago&Y-ywFdtoMJNFMM!ei|6AR1u{}s#XL-M_OKkP-gL`) z@yOJA=b3lQTv}#@N z{|YnFOQ)VRSWRB#Y_i%zi|g0P1M6-TY>D!a{d(`{{hk9~l#f@%U&*;F#3bwO+>zus zt5W)b^_H78e39X;PQlVk(pFfnpLFhzsC3N1L@BX~$&IsDI1(ZemnAki&(hezV$J*J?%xvm|4SOy9?_WG^yb>- GDIWk>%^qj~ literal 0 HcmV?d00001 diff --git a/xts/tests/data/ieee_vec3.blb b/xts/tests/data/ieee_vec3.blb new file mode 100644 index 0000000000000000000000000000000000000000..7a0a71a13c053ddae4a2780e9aa26eb4c9a66625 GIT binary patch literal 117 zcmZQ@`2X+kpWnZJ{`mgw>zB`;K7LR_0}93Y_E(CzX`c~uY$f`e56S(y@$({7P VH0iz%OC7gjuwvWN=~7Bnod8>ADG&ev literal 0 HcmV?d00001 diff --git a/xts/tests/data/ieee_vec4.blb b/xts/tests/data/ieee_vec4.blb new file mode 100644 index 0000000000000000000000000000000000000000..e705443b59f9b271c6d81c76456cb5145de06519 GIT binary patch literal 1079 zcmZQ@P?yk<&~TmLsvMl4QBr8CX6P8H7925svT{pOigC00R0Re!(9XcX$i&RT%Er#Y z$;HjX%f~MuC?qT*Dkd%=DJ3ltEX>ZXk=_+YG!U>X=QC= zYiIA^=;Z9;>gMj@>E-R?>*pU37!({58WtWA85JE98yBCDn3SB7nwFlCnU$TBo0nfu zSX5k6T2@|BSyf$ATUX!E*woz8+ScCD+11_C+t)u~;-tw_rcRqaW9F>cbLP&QzhL2_ z#Y>hhTfSoDs?}@Ou3NuhSIRu*8}wW`zwFVCevkK zOoWY|oJ#P}@?N;W+Afu$|LCo-~S6z6N_UO7G<5~maOrMX0|k&{!pzcf7|YReP`r2 zG+V_UoqWsu-$uG3;oH5kE9cHmIUzVB^nK9uQ|T{1KmT8}@#XZ^Q(SfB3P(5R9=ooy zW@^ca(|hzAF=EthD?EMF%$zlzCfYtJdJ>p|j%=+pqQ6)uy?Z(k{YuhW~uSi9X~WAAG(r&>wn fbM~K-io+|HNPObb(EX_(QvGV5liX9oxvUug(98|v literal 0 HcmV?d00001 diff --git a/xts/tests/data/ieee_vec5.blb b/xts/tests/data/ieee_vec5.blb new file mode 100644 index 0000000000000000000000000000000000000000..0bc223b03c479a97716827651da6b481f3594cee GIT binary patch literal 1079 zcmV-71jzdUKqnX|7$`-MMI%#SD0Fc(CNV)-CR13KlOu#;WHW;&l^_8C0000000000 z00000004&oC#Of7@1fLoNS`o_)X%2kscuWYG6?@AD1Fm`mKNxo$Bg&&K+Q8GbI3}-;{^YdR&ssQ7njN$LL_r zll}2#qDd08B9~!Ct{~2YYq#8+sY-TwuJQ~GVXXpXUa2j_#N--@QpQo!F?7CBp$tM1 z5a!qwi0$Yh1ENycr%>p2jqm^Mks^T#uDPF)a<7861l&uGQM|B%HKz156Eft=U`Q%U zp`bfHWdM)G+IF`K@o5dT>f)SV8?DehT+b?1*9G0phSW(>9P56=3-K!-qg#fIX0K!CW3FayWWn>9tbIg7UIe61OGf5cwqM4 zcGS+zl*kd7Rqs-l%4g~G=l^oC>6eAd3VwGW#j|b3*DS1+bjZuRFMu8+)HnF4xvS1RtkbkYpNn1R9*?sp3i5ahn67=&qet`VPl z0y?#f$_m#~ujW>}JQpQupKhD+pWRwG{DQmaa>Y}g+hCsb_naU;yA%&DSUj?=6||~b zWl$EVM3Q&g)OYVJjcfsZw>Ytl>q86(a=@?vvWd88Qf3Mei4AQXG*zCs@5F`Wfy zhX5u`Jg8A2l>WmK$jONxC!g=#V2um{dtgX9Kd2GbUge!q+2jtzXDxwKD>$~WiO!WD z51chw3yYVH7g7`RDZ3mm(+@9yFi9$5r81|VQPXc;F{jC)EGS@t|9vBr(T{jO8jf-C@y+A zI#O4i5uhmAO`O@saU}UtVr@`(kyfjH5s_N_D7WuD+7);m!ju*-*57cLbw{X(P0) zVuVZdpqx#izbva82%`*Q-oL8WIKC4|+-d~qExYBpXds--QD23A+nV;|C6{f=9z*5xPHV^MVh#*ALvJl$18TvIJ^CCFh-?%A!X zI?CQyh`4+2WE@PHOn{NKywivCmOr(;H>#2^zvJIdTajLFwG?rC-70hknox7~#J|*P z-+d@9dO12$SDg`{DB4Y&*~f7t`DBjcgR!i3IWXrIDvdYuP_p++gmTXbkRW5mvo4G+ zl90~(*EDS$cbX0L4&2^FG1>>PnZ`KiI(1n@-zx7KqZd;BGCj+25469#og%p zxRjo9nvlD%>d$|8dFQbdeki&Nu9OY8m8KgN?DXG_uzP|_uwsBL$%>UlyDF~jcgsJ< z+ZEi`*bRMK#0JEd3N(1FsEM00k(ldY!IS%9b*hY`Kr~8qsA8_ZlJ4Qo125T7w0(C3 zqlalDw5?)jmml#WQaW)K4>Cj`k{YSAn?@#tryx3SlOic57p{VIvfv|a!j1E^=Qhw>%e zhXDF^DmH+(U9a7TjXUKY;FRPB8MdtMl;)_XIKfd`Kv?YD-FkRHCA za~gaYwdUTz)~7B9ipiuVFNO>5`V{o&ApwGN4k}79l~CYDm||+{p==F7Fm%josW7I<%V9U z`&bs%AaGK>aVX7nJQBzehmoXnXc3cMxq_uKG@dctKpqQb3RaAvpC1*! zah9{n5~&_Yj`f{n^PnsAQaMSQ#usYiA;h8)>{L@_mmFO`PYKlP6Px|=2R$39CcL-#?3r~O6zo?t*8a)Q3}hOPygrv{eb;|14u x6bgEXKF!N%x&M=nib64lOlit2AVa>;2md}`UI9zbQGqzzIwi&8HWwN#0oL(W{UHDV literal 0 HcmV?d00001 diff --git a/xts/tests/data/ieee_vec7.blb b/xts/tests/data/ieee_vec7.blb new file mode 100644 index 0000000000000000000000000000000000000000..73cb7125b788a49f6b1f8b182bce52e063189557 GIT binary patch literal 1079 zcmV-71jzdUKqnX|7$`-MMI%#SD0Fc(CNV)-CR13KlOu#;WHW;&l_3290000000000 z00000004&ojzPDKISX3(*I2tDN@wZzzjr6Fn#K}9gu`|bIgg}Ws@X_rU=Guf`x?zi zSE*Y@Zes7BOE+>v(ZN#Zvkh|^tuV(H(iFD`uGyZowdH`b=!egAmqu1m)V2;_LIoY8 zX*@f4!9Dyo?d6wHa-(z!D+1$}%E;gAt@}ixIG+bujP-gL?XL|1#-lP#fuC^)U zXCbC-3VwK9J}IvylZWV??X|Xjyf?@nR`v{bB&uGM5*YYvOB%7cm zULFHDGyF{eqy7RDEqlXXBt~k?wH>x!#J_L?cFsNKkdMVer zF2KVUoZl*dTy_M!<^V~#`+JV6!4FZcBY8~gri-xiQ40ER3N25sga4($>n;R4{=}6+4x$Gix zt1Mp(d+2KarQXPT3+{o_)c^aTvsMKaU-NF*?bM_V%}XRY%@MM>hrPeAk*CVS zew1lQs z!AX5bzdlzPP%#uCwzo9Xiw(IRxKlB?9;x;fF!euXuI#Qg#_Fu6Q?Nm` zMdtS| zNbe8myk}m5!x*Ay{!oqABqT>D{`G+#XwabJ|#=vkbk>S-3#5gK`x>hyKU?ei4Flxlk zg80`Ajhn$=k&ovdbjBjp&v6%v_Tah9pDRJ#jlM)2LCp+vrrbxPNcM0qoerBD6fx;Y z;+eVZB66!NUkrQbYX7C)$a)Lzfz#Ch2kojW&&;z{1r=ZOZrJVAqz%nWBs$Fzvbl%7 zzps&}%EEq}TIy0>xgNMvF}WV8_7yPoMG~c@Dod?cp0<~P)i?4o zm%Bxq+R~JYDdANM^mu*?>07aL&tIz-+P9%P1uA1jJ(tYv8ZsGFh--Qhnfk8mt~JK$ ztfy13LAB-ZlfQ#Gf6^V+vjHGx{J>buGQ+T)bzW~!`8#qu}< ziZDgy_by2959qvSUV_6IqG;qfa3>q_8Mqtvse*K6RQOJEx$pvjqadJxLXtcq$b&*Z z`ALF*DZ8xP9L=yvIL=_0$y=tgB`Eaoc<$GDvf(1#{T#i~9UKa0gWwVTQ*C3=QziyC zhX8W#!R!7W;#qa3>pG}do!ToV9~_0ft3)cETP@Mo!@vh4P96oReS{_n=9ONG5ks5*m0bx>{*e7##Ew5Br-jtxID%$7a za6Ev8L&Bcc`!dPAavzYq#KC^)CB_>H19Ezt4PRWOV*C0E@xoC$ot;ZlMZG7b_U2RfQE5JF zYAcSdSJe^V4p_undyN2E_mYV9$HFhv@xOYBivy)&OllhA`TFJFPbq3Bma;y6)IQNq zrJ{g8O(^iw|IZ|Kc6YA5w0pCNH-`w6ij6@g?HD=W+9{ibiYNX zJ!ki66!V%}yFT2*G>nROLbD6|0k`#LEE4%H8@{ks^h&U{=%X)<7{ zvxdN}oduPJ!y49+`UV;Q1B%KlIjKWJ5mz=B7sQ=1ASkCsND({O$=B?6&pe62 x?1vc6|AiLWYc#{g6!8X`$*aWe1QAeTBfg<(^IV8J%-i7*PVWr;wN4WIBMOu12ipJu literal 0 HcmV?d00001 diff --git a/xts/tests/data/ieee_vec9.blb b/xts/tests/data/ieee_vec9.blb new file mode 100644 index 0000000000000000000000000000000000000000..9f63b1c45b82dc016247d32991c354133de9436b GIT binary patch literal 1079 zcmV-71jzdUKqnX|7$`-MMI%#SD0Fc(CNV)-CR13KlOu#;WHW;&l_38B0000000000 z00000004&oa__yTtXKFx34_F#@uu;v%K{uea0pr+Qhyxe9_5i5f&u|yQk>W)b9ODSR9W7X zpr|U^=izWXfQ3WCp4R&^+28~+d=pg|44GO7bQF`IoFTdaqlbtsr1m8=xO=f(V9}Q# z91qjKFu_m?KF{pkX4rBcki5jfe(5E~8wvw*dYlbkT%=&FQ97NSOH)O?C#Cl0 zQ}W)fOeX5X206;>#;0Q>toDFhDH53l{Z7_QEI zTtsOyV5+l*z@vK+u@v#d6|TEwJLG5=lql3XO^d_qIDtOOFrhs0yBO6Z@tG||*s-K8 zqYEPD$y*tnjTkS8K(L(!m4(9^){*)K8UF){$}BmlLqZW(HWwGfoiZROr$$H-JJ`wB z?03&RiNWlL7|#EN7TIew!^RZx2ARpL#O(wTP+}v#p=$G7h&s&M;Sf&m4E?oE68j?x zhX68Qu8pl9N;1XD{yMPhl~kQZ8fVVA<*F?@`(obC1ydP2r3`hxB_0UT(2o#^S8RU$ z`maAlm04FF5>XdZuzK)w^If27@XiarYmZ#aTr&SYkn5AUoH2 z{nLhZaXgKx-524?zT`5nmxs39^Ds_0w=uL`_1{sqkI%EyBxVAQ3!`OK=iwT#?gnNC x9Tr8|Vnj>CdN>$~FsMsSRuh%SJP2?hl6kXPnD~!ev!N4#)8XMa|E13Aq3O literal 0 HcmV?d00001 From 76997cd6d69ad019e0df221a4edc24150bfbe528 Mon Sep 17 00:00:00 2001 From: zer0x64 Date: Sat, 23 Nov 2024 20:24:53 -0500 Subject: [PATCH 09/23] [XTS] Fix parallel processing bug --- xts/src/xts_core.rs | 6 ++--- xts/tests/aes.rs | 58 ++++++++++++++++++++++++++++++++++++++------- 2 files changed, 53 insertions(+), 11 deletions(-) diff --git a/xts/src/xts_core.rs b/xts/src/xts_core.rs index ce55c5c..8af76ea 100644 --- a/xts/src/xts_core.rs +++ b/xts/src/xts_core.rs @@ -58,7 +58,7 @@ pub trait Xts: ParBlocksSizeUser + BlockSizeUser { let iv = self.get_iv_mut(); xor(block, iv); - let _ = gf_mul(iv); + gf_mul(iv); } /// Encrypt/decrypt a block using XTS and update the tweak @@ -79,7 +79,7 @@ pub trait Xts: ParBlocksSizeUser + BlockSizeUser { *i = iv.clone(); xor(b, iv); - let _ = gf_mul(iv); + gf_mul(iv); } } @@ -116,7 +116,7 @@ pub trait Xts: ParBlocksSizeUser + BlockSizeUser { fn process_tail_blocks(&mut self, blocks: InOutBuf<'_, '_, Block>) { for mut block in blocks { let mut b = block.clone_in(); - self.process_inplace(&mut b); + self.process_block_inplace(&mut b); *block.get_out() = b; } } diff --git a/xts/tests/aes.rs b/xts/tests/aes.rs index 081c75b..e1674f6 100644 --- a/xts/tests/aes.rs +++ b/xts/tests/aes.rs @@ -2,6 +2,44 @@ use aes::*; use xts::{Decryptor, Encryptor}; use cipher::{block_mode_dec_test, block_mode_enc_test}; +#[test] +fn manual_test() { + use cipher::{ + array::Array, blobby::Blob4Iterator, inout::InOutBuf, typenum::Unsigned, + BlockCipherEncrypt, BlockModeEncrypt, BlockSizeUser, KeyIvInit, + }; + + fn run_test(key: &[u8], iv: &[u8], pt: &[u8], ct: &[u8]) { + assert_eq!(pt.len(), ct.len()); + + let mut state = as KeyIvInit>::new_from_slices(key, iv).unwrap(); + let mut out = vec![0u8; ct.len()]; + let mut buf = InOutBuf::new(pt, &mut out).unwrap(); + let (blocks, tail) = buf.reborrow().into_chunks(); + + assert_eq!(tail.len(), 0); + + for block in blocks { + state.encrypt_block_inout(block); + } + + assert_eq!(buf.get_out(), ct); + + let mut state = as KeyIvInit>::new_from_slices(key, iv).unwrap(); + buf.get_out().iter_mut().for_each(|b| *b = 0); + let (blocks, _) = buf.reborrow().into_chunks(); + state.encrypt_blocks_inout(blocks); + + assert_eq!(buf.get_out(), ct); + } + + let data = include_bytes!(concat!("data/", "ieee_vec1", ".blb")); + for row in Blob4Iterator::new(data).unwrap() { + let [key, iv, pt, ct] = row.unwrap(); + run_test(key, iv, pt, ct); + } +} + // Test vectors from IEEE 1619-2018 block_mode_enc_test!(aes128_xts_enc_vec1_test, "ieee_vec1", Encryptor); block_mode_dec_test!(aes128_xts_dec_vec1_test, "ieee_vec1", Decryptor); @@ -31,13 +69,17 @@ block_mode_enc_test!(aes256_xts_enc_vec13_test, "ieee_vec13", Encryptor) block_mode_dec_test!(aes256_xts_dec_vec13_test, "ieee_vec13", Decryptor); block_mode_enc_test!(aes256_xts_enc_vec14_test, "ieee_vec14", Encryptor); block_mode_dec_test!(aes256_xts_dec_vec14_test, "ieee_vec14", Decryptor); -block_mode_enc_test!(aes128_xts_enc_vec15_test, "ieee_vec15", Encryptor); -block_mode_dec_test!(aes128_xts_dec_vec15_test, "ieee_vec15", Decryptor); -block_mode_enc_test!(aes128_xts_enc_vec16_test, "ieee_vec16", Encryptor); -block_mode_dec_test!(aes128_xts_dec_vec16_test, "ieee_vec16", Decryptor); -block_mode_enc_test!(aes128_xts_enc_vec17_test, "ieee_vec17", Encryptor); -block_mode_dec_test!(aes128_xts_dec_vec17_test, "ieee_vec17", Decryptor); -block_mode_enc_test!(aes128_xts_enc_vec18_test, "ieee_vec18", Encryptor); -block_mode_dec_test!(aes128_xts_dec_vec18_test, "ieee_vec18", Decryptor); + +// Those tests ciphertext stealing, which cannot be done using the macro, since the macro asserts +// that the plaintext/ciphertext length is a multiple of the block size +// block_mode_enc_test!(aes128_xts_enc_vec15_test, "ieee_vec15", Encryptor); +// block_mode_dec_test!(aes128_xts_dec_vec15_test, "ieee_vec15", Decryptor); +// block_mode_enc_test!(aes128_xts_enc_vec16_test, "ieee_vec16", Encryptor); +// block_mode_dec_test!(aes128_xts_dec_vec16_test, "ieee_vec16", Decryptor); +// block_mode_enc_test!(aes128_xts_enc_vec17_test, "ieee_vec17", Encryptor); +// block_mode_dec_test!(aes128_xts_dec_vec17_test, "ieee_vec17", Decryptor); +// block_mode_enc_test!(aes128_xts_enc_vec18_test, "ieee_vec18", Encryptor); +// block_mode_dec_test!(aes128_xts_dec_vec18_test, "ieee_vec18", Decryptor); + block_mode_enc_test!(aes128_xts_enc_vec19_test, "ieee_vec19", Encryptor); block_mode_dec_test!(aes128_xts_dec_vec19_test, "ieee_vec19", Decryptor); From be14363146d5fc0fdc9fe4f51743d45ad37ac5c2 Mon Sep 17 00:00:00 2001 From: zer0x64 Date: Sat, 23 Nov 2024 20:38:55 -0500 Subject: [PATCH 10/23] [XTS] Add benchmark --- xts/benches/aes128.rs | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) create mode 100644 xts/benches/aes128.rs diff --git a/xts/benches/aes128.rs b/xts/benches/aes128.rs new file mode 100644 index 0000000..ede3d31 --- /dev/null +++ b/xts/benches/aes128.rs @@ -0,0 +1,28 @@ +#![feature(test)] +extern crate test; + +use aes::{ Aes128, Aes256 }; + +cipher::block_encryptor_bench!( + KeyIv: xts::Encryptor, + xts_aes128_encrypt_block, + xts_aes128_encrypt_blocks, +); + +cipher::block_decryptor_bench!( + KeyIv: xts::Decryptor, + xts_aes128_decrypt_block, + xts_aes128_decrypt_blocks, +); + +cipher::block_encryptor_bench!( + KeyIv: xts::Encryptor, + xts_aes256_encrypt_block, + xts_aes256_encrypt_blocks, +); + +cipher::block_decryptor_bench!( + KeyIv: xts::Decryptor, + xts_aes256_decrypt_block, + xts_aes256_decrypt_blocks, +); From d62097cf3e527896c7f0b97d841c169a0b953fdb Mon Sep 17 00:00:00 2001 From: zer0x64 Date: Sun, 24 Nov 2024 13:49:49 -0500 Subject: [PATCH 11/23] [XTS] Fully split cipher and tweak encryptor --- xts/benches/aes128.rs | 8 ++-- xts/src/decrypt.rs | 70 +++++++++++++++------------- xts/src/encrypt.rs | 104 +++++++++++++++++++++++++++--------------- xts/src/lib.rs | 4 +- xts/tests/aes.rs | 38 +++++++-------- 5 files changed, 131 insertions(+), 93 deletions(-) diff --git a/xts/benches/aes128.rs b/xts/benches/aes128.rs index ede3d31..a4f9905 100644 --- a/xts/benches/aes128.rs +++ b/xts/benches/aes128.rs @@ -4,25 +4,25 @@ extern crate test; use aes::{ Aes128, Aes256 }; cipher::block_encryptor_bench!( - KeyIv: xts::Encryptor, + KeyIv: xts::SplitEncryptor, xts_aes128_encrypt_block, xts_aes128_encrypt_blocks, ); cipher::block_decryptor_bench!( - KeyIv: xts::Decryptor, + KeyIv: xts::SplitDecryptor, xts_aes128_decrypt_block, xts_aes128_decrypt_blocks, ); cipher::block_encryptor_bench!( - KeyIv: xts::Encryptor, + KeyIv: xts::SplitEncryptor, xts_aes256_encrypt_block, xts_aes256_encrypt_blocks, ); cipher::block_decryptor_bench!( - KeyIv: xts::Decryptor, + KeyIv: xts::SplitDecryptor, xts_aes256_decrypt_block, xts_aes256_decrypt_blocks, ); diff --git a/xts/src/decrypt.rs b/xts/src/decrypt.rs index 6b7e3fb..fefd342 100644 --- a/xts/src/decrypt.rs +++ b/xts/src/decrypt.rs @@ -1,22 +1,26 @@ +use core::{fmt, ops::Add}; + use crate::xts_core::{precompute_iv, Xts}; use cipher::{ array::ArraySize, - consts::B1, crypto_common::{BlockSizes, IvSizeUser}, inout::InOut, - typenum::Double, + typenum::Sum, AlgorithmName, Block, BlockCipherDecBackend, BlockCipherDecClosure, BlockCipherDecrypt, BlockCipherEncrypt, BlockModeDecBackend, BlockModeDecClosure, BlockModeDecrypt, BlockSizeUser, Iv, IvState, Key, KeyInit, KeyIvInit, KeySizeUser, ParBlocks, ParBlocksSizeUser, }; -use core::{fmt, ops::Shl}; #[cfg(feature = "zeroize")] use cipher::zeroize::{Zeroize, ZeroizeOnDrop}; +/// XTS mode decryptor +pub type Decryptor = SplitDecryptor; + /// XTS mode decryptor. +/// This structure allows using different cipher for the cipher itself and the tweak. #[derive(Clone)] -pub struct Decryptor +pub struct SplitDecryptor where C: BlockCipherDecrypt, T: BlockCipherEncrypt, @@ -26,46 +30,50 @@ where iv: Block, } -impl KeySizeUser for Decryptor +impl KeySizeUser for SplitDecryptor where - KS: ArraySize + Shl, - >::Output: ArraySize, + KS1: ArraySize + Add, + KS2: ArraySize, + >::Output: ArraySize, BS: BlockSizes, - C: BlockCipherDecrypt + KeySizeUser, - T: BlockCipherEncrypt + KeySizeUser, + C: BlockCipherDecrypt + KeySizeUser, + T: BlockCipherEncrypt + KeySizeUser, { - type KeySize = Double; + type KeySize = Sum; } -impl KeyIvInit for Decryptor +impl KeyIvInit for SplitDecryptor where - KS: ArraySize, BS: BlockSizes, - C: BlockCipherDecrypt + KeySizeUser + KeyInit, - T: BlockCipherEncrypt + KeySizeUser + KeyInit, - Decryptor: KeySizeUser, + C: BlockCipherDecrypt + KeySizeUser + KeyInit, + T: BlockCipherEncrypt + KeySizeUser + KeyInit, + SplitDecryptor: KeySizeUser, { + /// Create a new instance of the cipher and initialize it. + /// This assumes a single key, which is a concatenation of the cipher key and the tweak key, in that order. + /// To use different key, use `new_from_split_keys` fn new(key: &Key, iv: &Iv) -> Self { // Split the key and call split key constructor + // Assumes the key is cipher_key + tweak_key let k1 = <&Key>::try_from(&key[..C::key_size()]) .expect("Due to trait bounds, k1 should always be half the size of the XTS key"); - let k2 = <&Key>::try_from(&key[T::key_size()..]) + let k2 = <&Key>::try_from(&key[C::key_size()..]) .expect("Due to trait bounds, k2 should always be half the size of the XTS key"); Self::new_from_split_keys(k1, k2, iv) } } -impl Decryptor +impl SplitDecryptor where BS: BlockSizes, C: BlockCipherDecrypt + KeyInit + KeySizeUser, T: BlockCipherEncrypt + KeyInit + KeySizeUser, { /// Create an XTS array and precompute it - pub fn new_from_split_keys(k1: &Key, k2: &Key, iv: &Block) -> Self { - let cipher = C::new(&k1); - let tweaker = T::new(&k2); + pub fn new_from_split_keys(cipher_key: &Key, tweak_key: &Key, iv: &Block) -> Self { + let cipher = C::new(&cipher_key); + let tweaker = T::new(&tweak_key); let iv = precompute_iv(&tweaker, &iv); Self { @@ -75,13 +83,13 @@ where } } - /// Change the IV + /// Change the IV/sector number pub fn reset_iv(&mut self, iv: &Block) { self.iv = precompute_iv(&self.tweaker, iv) } } -impl BlockSizeUser for Decryptor +impl BlockSizeUser for SplitDecryptor where BS: BlockSizes, C: BlockCipherDecrypt, @@ -90,7 +98,7 @@ where type BlockSize = C::BlockSize; } -impl BlockModeDecrypt for Decryptor +impl BlockModeDecrypt for SplitDecryptor where BS: BlockSizes, C: BlockCipherDecrypt, @@ -139,7 +147,7 @@ where } } -impl IvSizeUser for Decryptor +impl IvSizeUser for SplitDecryptor where BS: BlockSizes, C: BlockCipherDecrypt, @@ -148,7 +156,7 @@ where type IvSize = C::BlockSize; } -impl IvState for Decryptor +impl IvState for SplitDecryptor where BS: BlockSizes, C: BlockCipherDecrypt, @@ -160,7 +168,7 @@ where } } -impl AlgorithmName for Decryptor +impl AlgorithmName for SplitDecryptor where BS: BlockSizes, C: BlockCipherDecrypt + AlgorithmName, @@ -175,20 +183,18 @@ where } } -impl fmt::Debug for Decryptor +impl fmt::Debug for SplitDecryptor where BS: BlockSizes, C: BlockCipherDecrypt + AlgorithmName, T: BlockCipherEncrypt + AlgorithmName, { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.write_str("xts::Decryptor<")?; - ::write_alg_name(f)?; - f.write_str("> { ... }") + Self::write_alg_name(f) } } -impl Drop for Decryptor +impl Drop for SplitDecryptor where C: BlockCipherDecrypt, T: BlockCipherEncrypt, @@ -200,7 +206,7 @@ where } #[cfg(feature = "zeroize")] -impl ZeroizeOnDrop for Decryptor +impl ZeroizeOnDrop for SplitDecryptor where C: BlockCipherDecrypt + ZeroizeOnDrop, T: BlockCipherEncrypt + ZeroizeOnDrop, diff --git a/xts/src/encrypt.rs b/xts/src/encrypt.rs index 4d8a6fc..c35abdf 100644 --- a/xts/src/encrypt.rs +++ b/xts/src/encrypt.rs @@ -1,61 +1,77 @@ use crate::xts_core::{precompute_iv, Xts}; use cipher::{ - array::ArraySize, consts::B1, crypto_common::BlockSizes, typenum::Double, AlgorithmName, + array::ArraySize, crypto_common::BlockSizes, typenum::Sum, AlgorithmName, Block, BlockCipherEncBackend, BlockCipherEncClosure, BlockCipherEncrypt, BlockModeEncBackend, BlockModeEncClosure, BlockModeEncrypt, BlockSizeUser, InOut, Iv, IvSizeUser, IvState, Key, KeyInit, KeyIvInit, KeySizeUser, ParBlocks, ParBlocksSizeUser, }; -use core::{fmt, ops::Shl}; +use core::{fmt, ops::Add}; #[cfg(feature = "zeroize")] use cipher::zeroize::{Zeroize, ZeroizeOnDrop}; /// XTS mode encryptor. +pub type Encryptor = SplitEncryptor; + +/// XTS mode encryptor. +/// This structure allows using different cipher for the cipher itself and the tweak. #[derive(Clone)] -pub struct Encryptor +pub struct SplitEncryptor where C: BlockCipherEncrypt, + T: BlockCipherEncrypt, { cipher: C, - tweaker: C, + tweaker: T, iv: Block, } // This would probably be the cleanest way to do it, but it would require a way to multiply a typenum by 2 -impl KeySizeUser for Encryptor +impl KeySizeUser for SplitEncryptor where - KS: ArraySize + Shl, - >::Output: ArraySize, - C: BlockCipherEncrypt + KeySizeUser, + KS1: ArraySize + Add, + KS2: ArraySize, + >::Output: ArraySize, + BS: BlockSizes, + C: BlockCipherEncrypt + KeySizeUser, + T: BlockCipherEncrypt + KeySizeUser, { - type KeySize = Double; + type KeySize = Sum; } -impl KeyIvInit for Encryptor +impl KeyIvInit for SplitEncryptor where - C: BlockCipherEncrypt + KeySizeUser + KeyInit, - Encryptor: KeySizeUser, + BS: BlockSizes, + C: BlockCipherEncrypt + KeySizeUser + KeyInit, + T: BlockCipherEncrypt + KeySizeUser + KeyInit, + SplitEncryptor: KeySizeUser, { + /// Create a new instance of the cipher and initialize it. + /// This assumes a single key, which is a concatenation of the cipher key and the tweak key, in that order. + /// To use different key, use `new_from_split_keys` fn new(key: &Key, iv: &Iv) -> Self { // Split the key and call split key constructor + // Assumes the key is cipher_key + tweak_key let k1 = <&Key>::try_from(&key[..C::key_size()]) .expect("Due to trait bounds, k1 should always be half the size of the XTS key"); - let k2 = <&Key>::try_from(&key[C::key_size()..]) + let k2 = <&Key>::try_from(&key[C::key_size()..]) .expect("Due to trait bounds, k2 should always be half the size of the XTS key"); Self::new_from_split_keys(k1, k2, iv) } } -impl Encryptor +impl SplitEncryptor where - C: BlockCipherEncrypt + KeyInit + KeySizeUser, + BS: BlockSizes, + C: BlockCipherEncrypt + KeyInit + KeySizeUser, + T: BlockCipherEncrypt + KeyInit + KeySizeUser, { /// Create an XTS array and precompute it - pub fn new_from_split_keys(k1: &Key, k2: &Key, iv: &Block) -> Self { + pub fn new_from_split_keys(k1: &Key, k2: &Key, iv: &Block) -> Self { let cipher = C::new(&k1); - let tweaker = C::new(&k2); + let tweaker = T::new(&k2); let iv = precompute_iv(&tweaker, &iv); Self { @@ -65,29 +81,35 @@ where } } - /// Change the IV + /// Change the IV/sector number pub fn reset_iv(&mut self, iv: &Block) { self.iv = precompute_iv(&self.tweaker, iv) } } -impl BlockSizeUser for Encryptor +impl BlockSizeUser for SplitEncryptor where - C: BlockCipherEncrypt, + BS: BlockSizes, + C: BlockCipherEncrypt, + T: BlockCipherEncrypt, { type BlockSize = C::BlockSize; } -impl IvSizeUser for Encryptor +impl IvSizeUser for SplitEncryptor where - C: BlockCipherEncrypt, + BS: BlockSizes, + C: BlockCipherEncrypt, + T: BlockCipherEncrypt, { - type IvSize = C::BlockSize; + type IvSize = BS; } -impl IvState for Encryptor +impl IvState for SplitEncryptor where - C: BlockCipherEncrypt, + BS: BlockSizes, + C: BlockCipherEncrypt, + T: BlockCipherEncrypt, { #[inline] fn iv_state(&self) -> Iv { @@ -95,9 +117,11 @@ where } } -impl BlockModeEncrypt for Encryptor +impl BlockModeEncrypt for SplitEncryptor where - C: BlockCipherEncrypt, + BS: BlockSizes, + C: BlockCipherEncrypt, + T: BlockCipherEncrypt, { fn encrypt_with_backend(&mut self, f: impl BlockModeEncClosure) { struct Closure<'a, BS, BM> @@ -141,29 +165,37 @@ where } } -impl AlgorithmName for Encryptor +impl AlgorithmName for SplitEncryptor where - C: BlockCipherEncrypt + AlgorithmName, + BS: BlockSizes, + C: BlockCipherEncrypt + AlgorithmName, + T: BlockCipherEncrypt + AlgorithmName, { fn write_alg_name(f: &mut fmt::Formatter<'_>) -> fmt::Result { f.write_str("xts::Encryptor<")?; ::write_alg_name(f)?; + f.write_str(",")?; + ::write_alg_name(f)?; f.write_str(">") } } -impl fmt::Debug for Encryptor +impl fmt::Debug for SplitEncryptor where - C: BlockCipherEncrypt + AlgorithmName, + BS: BlockSizes, + C: BlockCipherEncrypt + AlgorithmName, + T: BlockCipherEncrypt + AlgorithmName, { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.write_str("xts::Encryptor<")?; - ::write_alg_name(f)?; - f.write_str("> { ... }") + Self::write_alg_name(f) } } -impl Drop for Encryptor { +impl Drop for SplitEncryptor +where + C: BlockCipherEncrypt, + T: BlockCipherEncrypt, +{ fn drop(&mut self) { #[cfg(feature = "zeroize")] self.iv.zeroize(); @@ -171,7 +203,7 @@ impl Drop for Encryptor { } #[cfg(feature = "zeroize")] -impl ZeroizeOnDrop for Encryptor {} +impl ZeroizeOnDrop for SplitEncryptor {} struct Backend<'a, BS, BC> where diff --git a/xts/src/lib.rs b/xts/src/lib.rs index fbea506..cdb888e 100644 --- a/xts/src/lib.rs +++ b/xts/src/lib.rs @@ -98,8 +98,8 @@ mod encrypt; mod gf; mod xts_core; -pub use decrypt::Decryptor; -pub use encrypt::Encryptor; +pub use decrypt::{Decryptor, SplitDecryptor}; +pub use encrypt::{Encryptor, SplitEncryptor}; /// Error which indicates that message is smaller than cipher's block size. #[derive(Copy, Clone, Debug)] diff --git a/xts/tests/aes.rs b/xts/tests/aes.rs index e1674f6..c18532b 100644 --- a/xts/tests/aes.rs +++ b/xts/tests/aes.rs @@ -42,44 +42,44 @@ fn manual_test() { // Test vectors from IEEE 1619-2018 block_mode_enc_test!(aes128_xts_enc_vec1_test, "ieee_vec1", Encryptor); -block_mode_dec_test!(aes128_xts_dec_vec1_test, "ieee_vec1", Decryptor); +block_mode_dec_test!(aes128_xts_dec_vec1_test, "ieee_vec1", Decryptor); block_mode_enc_test!(aes128_xts_enc_vec2_test, "ieee_vec2", Encryptor); -block_mode_dec_test!(aes128_xts_dec_vec2_test, "ieee_vec2", Decryptor); +block_mode_dec_test!(aes128_xts_dec_vec2_test, "ieee_vec2", Decryptor); block_mode_enc_test!(aes128_xts_enc_vec3_test, "ieee_vec3", Encryptor); -block_mode_dec_test!(aes128_xts_dec_vec3_test, "ieee_vec3", Decryptor); +block_mode_dec_test!(aes128_xts_dec_vec3_test, "ieee_vec3", Decryptor); block_mode_enc_test!(aes128_xts_enc_vec4_test, "ieee_vec4", Encryptor); -block_mode_dec_test!(aes128_xts_dec_vec4_test, "ieee_vec4", Decryptor); +block_mode_dec_test!(aes128_xts_dec_vec4_test, "ieee_vec4", Decryptor); block_mode_enc_test!(aes128_xts_enc_vec5_test, "ieee_vec5", Encryptor); -block_mode_dec_test!(aes128_xts_dec_vec5_test, "ieee_vec5", Decryptor); +block_mode_dec_test!(aes128_xts_dec_vec5_test, "ieee_vec5", Decryptor); block_mode_enc_test!(aes128_xts_enc_vec6_test, "ieee_vec6", Encryptor); -block_mode_dec_test!(aes128_xts_dec_vec6_test, "ieee_vec6", Decryptor); +block_mode_dec_test!(aes128_xts_dec_vec6_test, "ieee_vec6", Decryptor); block_mode_enc_test!(aes128_xts_enc_vec7_test, "ieee_vec7", Encryptor); -block_mode_dec_test!(aes128_xts_dec_vec7_test, "ieee_vec7", Decryptor); +block_mode_dec_test!(aes128_xts_dec_vec7_test, "ieee_vec7", Decryptor); block_mode_enc_test!(aes128_xts_enc_vec8_test, "ieee_vec8", Encryptor); -block_mode_dec_test!(aes128_xts_dec_vec8_test, "ieee_vec8", Decryptor); +block_mode_dec_test!(aes128_xts_dec_vec8_test, "ieee_vec8", Decryptor); block_mode_enc_test!(aes128_xts_enc_vec9_test, "ieee_vec9", Encryptor); -block_mode_dec_test!(aes128_xts_dec_vec9_test, "ieee_vec9", Decryptor); +block_mode_dec_test!(aes128_xts_dec_vec9_test, "ieee_vec9", Decryptor); block_mode_enc_test!(aes256_xts_enc_vec10_test, "ieee_vec10", Encryptor); -block_mode_dec_test!(aes256_xts_dec_vec10_test, "ieee_vec10", Decryptor); +block_mode_dec_test!(aes256_xts_dec_vec10_test, "ieee_vec10", Decryptor); block_mode_enc_test!(aes256_xts_enc_vec11_test, "ieee_vec11", Encryptor); -block_mode_dec_test!(aes256_xts_dec_vec11_test, "ieee_vec11", Decryptor); +block_mode_dec_test!(aes256_xts_dec_vec11_test, "ieee_vec11", Decryptor); block_mode_enc_test!(aes256_xts_enc_vec12_test, "ieee_vec12", Encryptor); -block_mode_dec_test!(aes256_xts_dec_vec12_test, "ieee_vec12", Decryptor); +block_mode_dec_test!(aes256_xts_dec_vec12_test, "ieee_vec12", Decryptor); block_mode_enc_test!(aes256_xts_enc_vec13_test, "ieee_vec13", Encryptor); -block_mode_dec_test!(aes256_xts_dec_vec13_test, "ieee_vec13", Decryptor); +block_mode_dec_test!(aes256_xts_dec_vec13_test, "ieee_vec13", Decryptor); block_mode_enc_test!(aes256_xts_enc_vec14_test, "ieee_vec14", Encryptor); -block_mode_dec_test!(aes256_xts_dec_vec14_test, "ieee_vec14", Decryptor); +block_mode_dec_test!(aes256_xts_dec_vec14_test, "ieee_vec14", Decryptor); // Those tests ciphertext stealing, which cannot be done using the macro, since the macro asserts // that the plaintext/ciphertext length is a multiple of the block size // block_mode_enc_test!(aes128_xts_enc_vec15_test, "ieee_vec15", Encryptor); -// block_mode_dec_test!(aes128_xts_dec_vec15_test, "ieee_vec15", Decryptor); +// block_mode_dec_test!(aes128_xts_dec_vec15_test, "ieee_vec15", Decryptor); // block_mode_enc_test!(aes128_xts_enc_vec16_test, "ieee_vec16", Encryptor); -// block_mode_dec_test!(aes128_xts_dec_vec16_test, "ieee_vec16", Decryptor); +// block_mode_dec_test!(aes128_xts_dec_vec16_test, "ieee_vec16", Decryptor); // block_mode_enc_test!(aes128_xts_enc_vec17_test, "ieee_vec17", Encryptor); -// block_mode_dec_test!(aes128_xts_dec_vec17_test, "ieee_vec17", Decryptor); +// block_mode_dec_test!(aes128_xts_dec_vec17_test, "ieee_vec17", Decryptor); // block_mode_enc_test!(aes128_xts_enc_vec18_test, "ieee_vec18", Encryptor); -// block_mode_dec_test!(aes128_xts_dec_vec18_test, "ieee_vec18", Decryptor); +// block_mode_dec_test!(aes128_xts_dec_vec18_test, "ieee_vec18", Decryptor); block_mode_enc_test!(aes128_xts_enc_vec19_test, "ieee_vec19", Encryptor); -block_mode_dec_test!(aes128_xts_dec_vec19_test, "ieee_vec19", Decryptor); +block_mode_dec_test!(aes128_xts_dec_vec19_test, "ieee_vec19", Decryptor); From b7126d9513ba9c7ecfc5923b73cce9f4a48d8605 Mon Sep 17 00:00:00 2001 From: zer0x64 Date: Sun, 24 Nov 2024 14:10:22 -0500 Subject: [PATCH 12/23] [XTS] Fix benchmarks --- xts/benches/{aes128.rs => aes.rs} | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) rename xts/benches/{aes128.rs => aes.rs} (72%) diff --git a/xts/benches/aes128.rs b/xts/benches/aes.rs similarity index 72% rename from xts/benches/aes128.rs rename to xts/benches/aes.rs index a4f9905..e44e6f4 100644 --- a/xts/benches/aes128.rs +++ b/xts/benches/aes.rs @@ -4,25 +4,25 @@ extern crate test; use aes::{ Aes128, Aes256 }; cipher::block_encryptor_bench!( - KeyIv: xts::SplitEncryptor, + KeyIv: xts::Encryptor, xts_aes128_encrypt_block, xts_aes128_encrypt_blocks, ); cipher::block_decryptor_bench!( - KeyIv: xts::SplitDecryptor, + KeyIv: xts::Decryptor, xts_aes128_decrypt_block, xts_aes128_decrypt_blocks, ); cipher::block_encryptor_bench!( - KeyIv: xts::SplitEncryptor, + KeyIv: xts::Encryptor, xts_aes256_encrypt_block, xts_aes256_encrypt_blocks, ); cipher::block_decryptor_bench!( - KeyIv: xts::SplitDecryptor, + KeyIv: xts::Decryptor, xts_aes256_decrypt_block, xts_aes256_decrypt_blocks, ); From aac14b2ee3062db1dcd0c6abe3703d9ddffc09e6 Mon Sep 17 00:00:00 2001 From: zer0x64 Date: Sun, 24 Nov 2024 14:53:31 -0500 Subject: [PATCH 13/23] [XTS] Integrate ciphertext stealing --- xts/benches/aes.rs | 2 +- xts/src/decrypt.rs | 62 +++++++++++++++++++++++++++++++++++------ xts/src/encrypt.rs | 67 +++++++++++++++++++++++++++++++++++++-------- xts/src/xts_core.rs | 60 ++++++++-------------------------------- xts/tests/aes.rs | 2 +- 5 files changed, 123 insertions(+), 70 deletions(-) diff --git a/xts/benches/aes.rs b/xts/benches/aes.rs index e44e6f4..e131f22 100644 --- a/xts/benches/aes.rs +++ b/xts/benches/aes.rs @@ -1,7 +1,7 @@ #![feature(test)] extern crate test; -use aes::{ Aes128, Aes256 }; +use aes::{Aes128, Aes256}; cipher::block_encryptor_bench!( KeyIv: xts::Encryptor, diff --git a/xts/src/decrypt.rs b/xts/src/decrypt.rs index fefd342..ffdfa6d 100644 --- a/xts/src/decrypt.rs +++ b/xts/src/decrypt.rs @@ -1,6 +1,7 @@ use core::{fmt, ops::Add}; -use crate::xts_core::{precompute_iv, Xts}; +use crate::xts_core::{precompute_iv, Stealer, Xts}; +use crate::{Error, Result}; use cipher::{ array::ArraySize, crypto_common::{BlockSizes, IvSizeUser}, @@ -8,7 +9,7 @@ use cipher::{ typenum::Sum, AlgorithmName, Block, BlockCipherDecBackend, BlockCipherDecClosure, BlockCipherDecrypt, BlockCipherEncrypt, BlockModeDecBackend, BlockModeDecClosure, BlockModeDecrypt, BlockSizeUser, - Iv, IvState, Key, KeyInit, KeyIvInit, KeySizeUser, ParBlocks, ParBlocksSizeUser, + InOutBuf, Iv, IvState, Key, KeyInit, KeyIvInit, KeySizeUser, ParBlocks, ParBlocksSizeUser, }; #[cfg(feature = "zeroize")] @@ -87,6 +88,34 @@ where pub fn reset_iv(&mut self, iv: &Block) { self.iv = precompute_iv(&self.tweaker, iv) } + + /// Decrypt `inout` buffer. + pub fn decrypt_inout(&mut self, mut buf: InOutBuf<'_, '_, u8>) -> Result<()> { + if buf.len() < C::BlockSize::USIZE { + return Err(Error); + }; + + { + let (blocks, _) = buf.reborrow().into_chunks(); + self.decrypt_blocks_inout(blocks); + } + + self.ciphertext_stealing(buf.get_out()); + + Ok(()) + } + + /// Decrypt data in-place. + fn decrypt(&mut self, buf: &mut [u8]) -> Result<()> { + self.decrypt_inout(buf.into()) + } + + /// Decrypt data buffer-to-buffer. + fn decrypt_b2b(&mut self, in_buf: &[u8], out_buf: &mut [u8]) -> Result<()> { + InOutBuf::new(in_buf, out_buf) + .map_err(|_| Error) + .and_then(|buf| self.decrypt_inout(buf)) + } } impl BlockSizeUser for SplitDecryptor @@ -95,7 +124,7 @@ where C: BlockCipherDecrypt, T: BlockCipherEncrypt, { - type BlockSize = C::BlockSize; + type BlockSize = BS; } impl BlockModeDecrypt for SplitDecryptor @@ -147,13 +176,33 @@ where } } +impl Stealer for SplitDecryptor +where + BS: BlockSizes, + C: BlockCipherDecrypt, + T: BlockCipherEncrypt, +{ + fn process_block(&self, block: &mut Block) { + self.cipher.decrypt_block(block); + } + + fn get_iv_mut(&mut self) -> &mut Block { + &mut self.iv + } + + #[inline(always)] + fn is_decrypt() -> bool { + false + } +} + impl IvSizeUser for SplitDecryptor where BS: BlockSizes, C: BlockCipherDecrypt, T: BlockCipherEncrypt, { - type IvSize = C::BlockSize; + type IvSize = BS; } impl IvState for SplitDecryptor @@ -290,9 +339,4 @@ where fn get_iv_mut(&mut self) -> &mut Block { self.iv } - - #[inline(always)] - fn is_decrypt() -> bool { - true - } } diff --git a/xts/src/encrypt.rs b/xts/src/encrypt.rs index c35abdf..31dd474 100644 --- a/xts/src/encrypt.rs +++ b/xts/src/encrypt.rs @@ -1,8 +1,10 @@ -use crate::xts_core::{precompute_iv, Xts}; +use crate::xts_core::{precompute_iv, Stealer, Xts}; +use crate::{Error, Result}; +use cipher::InOutBuf; use cipher::{ - array::ArraySize, crypto_common::BlockSizes, typenum::Sum, AlgorithmName, - Block, BlockCipherEncBackend, BlockCipherEncClosure, BlockCipherEncrypt, BlockModeEncBackend, + array::ArraySize, crypto_common::BlockSizes, typenum::Sum, AlgorithmName, Block, + BlockCipherEncBackend, BlockCipherEncClosure, BlockCipherEncrypt, BlockModeEncBackend, BlockModeEncClosure, BlockModeEncrypt, BlockSizeUser, InOut, Iv, IvSizeUser, IvState, Key, KeyInit, KeyIvInit, KeySizeUser, ParBlocks, ParBlocksSizeUser, }; @@ -85,6 +87,34 @@ where pub fn reset_iv(&mut self, iv: &Block) { self.iv = precompute_iv(&self.tweaker, iv) } + + /// Encrypt `inout` buffer. + pub fn encrypt_inout(&mut self, mut buf: InOutBuf<'_, '_, u8>) -> Result<()> { + if buf.len() < C::BlockSize::USIZE { + return Err(Error); + }; + + { + let (blocks, _) = buf.reborrow().into_chunks(); + self.encrypt_blocks_inout(blocks); + } + + self.ciphertext_stealing(buf.get_out()); + + Ok(()) + } + + /// Encrypt data in-place. + fn encrypt(&mut self, buf: &mut [u8]) -> Result<()> { + self.encrypt_inout(buf.into()) + } + + /// Encrypt data buffer-to-buffer. + fn encrypt_b2b(&mut self, in_buf: &[u8], out_buf: &mut [u8]) -> Result<()> { + InOutBuf::new(in_buf, out_buf) + .map_err(|_| Error) + .and_then(|buf| self.encrypt_inout(buf)) + } } impl BlockSizeUser for SplitEncryptor @@ -93,7 +123,7 @@ where C: BlockCipherEncrypt, T: BlockCipherEncrypt, { - type BlockSize = C::BlockSize; + type BlockSize = BS; } impl IvSizeUser for SplitEncryptor @@ -165,6 +195,26 @@ where } } +impl Stealer for SplitEncryptor +where + BS: BlockSizes, + C: BlockCipherEncrypt, + T: BlockCipherEncrypt, +{ + fn process_block(&self, block: &mut Block) { + self.cipher.encrypt_block(block); + } + + fn get_iv_mut(&mut self) -> &mut Block { + &mut self.iv + } + + #[inline(always)] + fn is_decrypt() -> bool { + false + } +} + impl AlgorithmName for SplitEncryptor where BS: BlockSizes, @@ -192,7 +242,7 @@ where } impl Drop for SplitEncryptor -where +where C: BlockCipherEncrypt, T: BlockCipherEncrypt, { @@ -280,11 +330,6 @@ where } fn get_iv_mut(&mut self) -> &mut Block { - self.iv - } - - #[inline(always)] - fn is_decrypt() -> bool { - false + &mut self.iv } } diff --git a/xts/src/xts_core.rs b/xts/src/xts_core.rs index 8af76ea..4677c2f 100644 --- a/xts/src/xts_core.rs +++ b/xts/src/xts_core.rs @@ -1,4 +1,5 @@ use cipher::typenum::Unsigned; +use cipher::BlockCipherDecrypt; use cipher::{ crypto_common::BlockSizes, Array, Block, BlockCipherEncrypt, BlockSizeUser, InOut, InOutBuf, ParBlocks, ParBlocksSizeUser, @@ -31,9 +32,6 @@ pub trait Xts: ParBlocksSizeUser + BlockSizeUser { /// Gets the IV reference. fn get_iv_mut(&mut self) -> &mut Array; - /// There is a slight difference regarding the tweak during decryption - fn is_decrypt() -> bool; - //Unused but keeping for now just in case fn _process(&self, mut block: InOut<'_, '_, Block>) { let mut b = block.clone_in(); @@ -122,51 +120,17 @@ pub trait Xts: ParBlocksSizeUser + BlockSizeUser { } } -pub trait XtsMode: Xts { - fn process_all_in_place(&mut self, buffer: &mut [u8]) -> Result<()> { - let block_size = Self::block_size(); - let par_blocks_size = Self::ParBlocksSize::USIZE; - - if buffer.len() < block_size { - return Err(Error); - } - - let need_stealing = buffer.len() % Self::block_size() == 0; - - let (buffer, remaining_buffer) = if need_stealing { - buffer.split_at_mut((buffer.len() / block_size - 1) * block_size) - } else { - (buffer, [0u8; 0].as_mut_slice()) - }; - - // Split buffer into blocks - let mut blocks = buffer - .chunks_exact_mut(block_size) - .map(|b| <&mut Block>::try_from(b).unwrap()); - - // Can't figure out how to get parblocks in place here, commenting for now - // if par_blocks_size > 1 { - // let mut blocks: alloc::vec::Vec<&mut Block> = blocks.collect(); +pub(crate) trait Stealer: BlockSizeUser { + /// Encrypt/Decrypt the block + fn process_block(&self, block: &mut Block); - // let mut par_blocks = blocks.chunks_exact_mut(par_blocks_size); - // for b in par_blocks { - // let mut b = <&mut ParBlocks>::try_from(*b).unwrap(); - // } - - // self.process_tail_blocks_inplace(tail); - // } else { - for b in blocks { - self.process_block_inplace(b); - } - //} - - if need_stealing { - self.ciphertext_stealing(remaining_buffer); - }; + /// Gets the IV reference. + fn get_iv_mut(&mut self) -> &mut Array; - Ok(()) - } + /// There is a slight difference regarding the tweak during decryption + fn is_decrypt() -> bool; + /// Perform the ciphertext stealing phase fn ciphertext_stealing(&mut self, buffer: &mut [u8]) { let remaining_bytes = buffer.len() - Self::block_size(); @@ -185,7 +149,7 @@ pub trait XtsMode: Xts { carry }; - self.process_inplace(second_to_last_block); + self.process_block(second_to_last_block); { let iv = self.get_iv_mut(); @@ -195,7 +159,7 @@ pub trait XtsMode: Xts { } } else { // Process normally - self.process_block_inplace(second_to_last_block); + self.process_block(second_to_last_block); } // We first swap the remaining bytes with the previous block's @@ -209,6 +173,6 @@ pub trait XtsMode: Xts { let second_to_last_block: &mut Block = (&mut buffer[0..Self::block_size()]) .try_into() .expect("Not a full block on second to last block!"); - self.process_block_inplace(second_to_last_block); + self.process_block(second_to_last_block); } } diff --git a/xts/tests/aes.rs b/xts/tests/aes.rs index c18532b..63653be 100644 --- a/xts/tests/aes.rs +++ b/xts/tests/aes.rs @@ -1,6 +1,6 @@ use aes::*; -use xts::{Decryptor, Encryptor}; use cipher::{block_mode_dec_test, block_mode_enc_test}; +use xts::{Decryptor, Encryptor}; #[test] fn manual_test() { From 6638ea92089627a2de84a8776b71f06c46ddf2ef Mon Sep 17 00:00:00 2001 From: zer0x64 Date: Sun, 24 Nov 2024 16:19:02 -0500 Subject: [PATCH 14/23] [XTS] Ciphertext stealing tests and bugfixes --- Cargo.toml | 4 +- xts/src/decrypt.rs | 31 +++++++++++---- xts/src/encrypt.rs | 29 ++++++++++---- xts/src/xts_core.rs | 31 +++++++++------ xts/tests/aes.rs | 60 +++++++---------------------- xts/tests/macros.rs | 94 +++++++++++++++++++++++++++++++++++++++++++++ 6 files changed, 174 insertions(+), 75 deletions(-) create mode 100644 xts/tests/macros.rs diff --git a/Cargo.toml b/Cargo.toml index 432a71c..20fd564 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -2,5 +2,5 @@ resolver = "2" members = ["belt-ctr", "cbc", "cts", "cfb8", "cfb-mode", "ctr", "ige", "ofb", "pcbc", "xts"] -[profile.dev] -opt-level = 2 +#[profile.dev] +#opt-level = 2 diff --git a/xts/src/decrypt.rs b/xts/src/decrypt.rs index ffdfa6d..122e604 100644 --- a/xts/src/decrypt.rs +++ b/xts/src/decrypt.rs @@ -91,27 +91,38 @@ where /// Decrypt `inout` buffer. pub fn decrypt_inout(&mut self, mut buf: InOutBuf<'_, '_, u8>) -> Result<()> { - if buf.len() < C::BlockSize::USIZE { + if buf.len() < BS::USIZE { return Err(Error); }; - { - let (blocks, _) = buf.reborrow().into_chunks(); + if buf.len() % BS::USIZE == 0 { + // No need for stealing + let (blocks, _) = buf.into_chunks(); self.decrypt_blocks_inout(blocks); - } + } else { + let full_blocks = (buf.len() / BS::USIZE - 1) * BS::USIZE; - self.ciphertext_stealing(buf.get_out()); + let (blocks, mut tail) = buf.split_at(full_blocks); + let (blocks, _) = blocks.into_chunks(); + self.decrypt_blocks_inout(blocks); + + for mut b in tail.reborrow() { + *b.get_out() = *b.get_in(); + } + + self.ciphertext_stealing(tail.get_out()); + } Ok(()) } /// Decrypt data in-place. - fn decrypt(&mut self, buf: &mut [u8]) -> Result<()> { + pub fn decrypt(&mut self, buf: &mut [u8]) -> Result<()> { self.decrypt_inout(buf.into()) } /// Decrypt data buffer-to-buffer. - fn decrypt_b2b(&mut self, in_buf: &[u8], out_buf: &mut [u8]) -> Result<()> { + pub fn decrypt_b2b(&mut self, in_buf: &[u8], out_buf: &mut [u8]) -> Result<()> { InOutBuf::new(in_buf, out_buf) .map_err(|_| Error) .and_then(|buf| self.decrypt_inout(buf)) @@ -186,13 +197,17 @@ where self.cipher.decrypt_block(block); } + fn get_iv(&self) -> &Block { + &self.iv + } + fn get_iv_mut(&mut self) -> &mut Block { &mut self.iv } #[inline(always)] fn is_decrypt() -> bool { - false + true } } diff --git a/xts/src/encrypt.rs b/xts/src/encrypt.rs index 31dd474..18ce7b5 100644 --- a/xts/src/encrypt.rs +++ b/xts/src/encrypt.rs @@ -90,27 +90,38 @@ where /// Encrypt `inout` buffer. pub fn encrypt_inout(&mut self, mut buf: InOutBuf<'_, '_, u8>) -> Result<()> { - if buf.len() < C::BlockSize::USIZE { + if buf.len() < BS::USIZE { return Err(Error); }; - { - let (blocks, _) = buf.reborrow().into_chunks(); + if buf.len() % BS::USIZE == 0 { + // No need for stealing + let (blocks, _) = buf.into_chunks(); self.encrypt_blocks_inout(blocks); - } + } else { + let full_blocks = (buf.len() / BS::USIZE - 1) * BS::USIZE; - self.ciphertext_stealing(buf.get_out()); + let (blocks, mut tail) = buf.split_at(full_blocks); + let (blocks, _) = blocks.into_chunks(); + self.encrypt_blocks_inout(blocks); + + for mut b in tail.reborrow() { + *b.get_out() = *b.get_in(); + } + + self.ciphertext_stealing(tail.get_out()); + } Ok(()) } /// Encrypt data in-place. - fn encrypt(&mut self, buf: &mut [u8]) -> Result<()> { + pub fn encrypt(&mut self, buf: &mut [u8]) -> Result<()> { self.encrypt_inout(buf.into()) } /// Encrypt data buffer-to-buffer. - fn encrypt_b2b(&mut self, in_buf: &[u8], out_buf: &mut [u8]) -> Result<()> { + pub fn encrypt_b2b(&mut self, in_buf: &[u8], out_buf: &mut [u8]) -> Result<()> { InOutBuf::new(in_buf, out_buf) .map_err(|_| Error) .and_then(|buf| self.encrypt_inout(buf)) @@ -205,6 +216,10 @@ where self.cipher.encrypt_block(block); } + fn get_iv(&self) -> &Block { + &self.iv + } + fn get_iv_mut(&mut self) -> &mut Block { &mut self.iv } diff --git a/xts/src/xts_core.rs b/xts/src/xts_core.rs index 4677c2f..9e19a50 100644 --- a/xts/src/xts_core.rs +++ b/xts/src/xts_core.rs @@ -124,17 +124,32 @@ pub(crate) trait Stealer: BlockSizeUser { /// Encrypt/Decrypt the block fn process_block(&self, block: &mut Block); + /// Gets the IV reference. + fn get_iv(&self) -> &Array; + /// Gets the IV reference. fn get_iv_mut(&mut self) -> &mut Array; /// There is a slight difference regarding the tweak during decryption fn is_decrypt() -> bool; + fn process_xts_block(&mut self, block: &mut Block) { + xor(block, &self.get_iv()); + self.process_block(block); + xor(block, &self.get_iv()); + + let iv = self.get_iv_mut(); + gf_mul(iv); + } + /// Perform the ciphertext stealing phase fn ciphertext_stealing(&mut self, buffer: &mut [u8]) { let remaining_bytes = buffer.len() - Self::block_size(); - let second_to_last_block: &mut Block = (&mut buffer[0..Self::block_size()]) + // We split the buffer into the two blocks + let (second_to_last_block, last_block) = buffer.split_at_mut(Self::block_size()); + + let second_to_last_block: &mut Block = second_to_last_block .try_into() .expect("Not a full block on second to last block!"); @@ -159,20 +174,12 @@ pub(crate) trait Stealer: BlockSizeUser { } } else { // Process normally - self.process_block(second_to_last_block); + self.process_xts_block(second_to_last_block); } // We first swap the remaining bytes with the previous block's - { - let (previous_block, current_block) = buffer.split_at_mut(Self::block_size()); - previous_block[..remaining_bytes] - .swap_with_slice(&mut current_block[..remaining_bytes]); - } + second_to_last_block[..remaining_bytes].swap_with_slice(last_block); - // We can then decrypt the final block, which is not located at the second to last block - let second_to_last_block: &mut Block = (&mut buffer[0..Self::block_size()]) - .try_into() - .expect("Not a full block on second to last block!"); - self.process_block(second_to_last_block); + self.process_xts_block(second_to_last_block); } } diff --git a/xts/tests/aes.rs b/xts/tests/aes.rs index 63653be..c5eff7b 100644 --- a/xts/tests/aes.rs +++ b/xts/tests/aes.rs @@ -2,43 +2,7 @@ use aes::*; use cipher::{block_mode_dec_test, block_mode_enc_test}; use xts::{Decryptor, Encryptor}; -#[test] -fn manual_test() { - use cipher::{ - array::Array, blobby::Blob4Iterator, inout::InOutBuf, typenum::Unsigned, - BlockCipherEncrypt, BlockModeEncrypt, BlockSizeUser, KeyIvInit, - }; - - fn run_test(key: &[u8], iv: &[u8], pt: &[u8], ct: &[u8]) { - assert_eq!(pt.len(), ct.len()); - - let mut state = as KeyIvInit>::new_from_slices(key, iv).unwrap(); - let mut out = vec![0u8; ct.len()]; - let mut buf = InOutBuf::new(pt, &mut out).unwrap(); - let (blocks, tail) = buf.reborrow().into_chunks(); - - assert_eq!(tail.len(), 0); - - for block in blocks { - state.encrypt_block_inout(block); - } - - assert_eq!(buf.get_out(), ct); - - let mut state = as KeyIvInit>::new_from_slices(key, iv).unwrap(); - buf.get_out().iter_mut().for_each(|b| *b = 0); - let (blocks, _) = buf.reborrow().into_chunks(); - state.encrypt_blocks_inout(blocks); - - assert_eq!(buf.get_out(), ct); - } - - let data = include_bytes!(concat!("data/", "ieee_vec1", ".blb")); - for row in Blob4Iterator::new(data).unwrap() { - let [key, iv, pt, ct] = row.unwrap(); - run_test(key, iv, pt, ct); - } -} +mod macros; // Test vectors from IEEE 1619-2018 block_mode_enc_test!(aes128_xts_enc_vec1_test, "ieee_vec1", Encryptor); @@ -70,16 +34,20 @@ block_mode_dec_test!(aes256_xts_dec_vec13_test, "ieee_vec13", Decryptor) block_mode_enc_test!(aes256_xts_enc_vec14_test, "ieee_vec14", Encryptor); block_mode_dec_test!(aes256_xts_dec_vec14_test, "ieee_vec14", Decryptor); -// Those tests ciphertext stealing, which cannot be done using the macro, since the macro asserts +// Those tests ciphertext stealing, which cannot be done using the `cipher` macro, since the macro asserts // that the plaintext/ciphertext length is a multiple of the block size -// block_mode_enc_test!(aes128_xts_enc_vec15_test, "ieee_vec15", Encryptor); -// block_mode_dec_test!(aes128_xts_dec_vec15_test, "ieee_vec15", Decryptor); -// block_mode_enc_test!(aes128_xts_enc_vec16_test, "ieee_vec16", Encryptor); -// block_mode_dec_test!(aes128_xts_dec_vec16_test, "ieee_vec16", Decryptor); -// block_mode_enc_test!(aes128_xts_enc_vec17_test, "ieee_vec17", Encryptor); -// block_mode_dec_test!(aes128_xts_dec_vec17_test, "ieee_vec17", Decryptor); -// block_mode_enc_test!(aes128_xts_enc_vec18_test, "ieee_vec18", Encryptor); -// block_mode_dec_test!(aes128_xts_dec_vec18_test, "ieee_vec18", Decryptor); +block_mode_enc_stealing_test!(aes128_xts_enc_vec15_test, "ieee_vec15", Aes128); +block_mode_dec_stealing_test!(aes128_xts_dec_vec15_test, "ieee_vec15", Aes128); +block_mode_enc_stealing_test!(aes128_xts_enc_vec16_test, "ieee_vec16", Aes128); +block_mode_dec_stealing_test!(aes128_xts_dec_vec16_test, "ieee_vec16", Aes128); +block_mode_enc_stealing_test!(aes128_xts_enc_vec17_test, "ieee_vec17", Aes128); +block_mode_dec_stealing_test!(aes128_xts_dec_vec17_test, "ieee_vec17", Aes128); +block_mode_enc_stealing_test!(aes128_xts_enc_vec18_test, "ieee_vec18", Aes128); +block_mode_dec_stealing_test!(aes128_xts_dec_vec18_test, "ieee_vec18", Aes128); + +// Those tests checks that the custom methods works without ciphertext stealing +block_mode_enc_stealing_test!(aes128_xts_enc_custom_api_test, "ieee_vec1", Aes128); +block_mode_dec_stealing_test!(aes128_xts_dec_custom_api_test, "ieee_vec1", Aes128); block_mode_enc_test!(aes128_xts_enc_vec19_test, "ieee_vec19", Encryptor); block_mode_dec_test!(aes128_xts_dec_vec19_test, "ieee_vec19", Decryptor); diff --git a/xts/tests/macros.rs b/xts/tests/macros.rs new file mode 100644 index 0000000..d684348 --- /dev/null +++ b/xts/tests/macros.rs @@ -0,0 +1,94 @@ +/// Pasted from the `cipher` crate and adapted to support XTS tests + +#[macro_export] +macro_rules! block_mode_enc_stealing_test { + ($name:ident, $test_name:expr, $cipher:ty $(,)?) => { + #[test] + fn $name() { + use cipher::{blobby::Blob4Iterator, KeyIvInit}; + + use xts::Encryptor; + + fn run_test(i: usize, key: &[u8], iv: &[u8], pt: &[u8], ct: &[u8]) { + assert_eq!(pt.len(), ct.len()); + // test block-by-block processing + + // MODIFICATION: We hardcode XTS encryptor here + let mut state = + as KeyIvInit>::new_from_slices(key, iv).unwrap(); + + let mut out = vec![0u8; ct.len()]; + + // MODIFICATION: We don't split the block and we call encrypt_directly + state.encrypt_b2b(pt, &mut out).unwrap(); + + // MODIFICATION: Crash here so we can get the actual output + if out != ct { + panic!( + "\n\ + Failed test №{}\n\ + key:\t{:?}\n\ + iv:\t{:?}\n\ + plaintext:\t{:?}\n\ + ciphertext:\t{:?}\n\ + actual_ct:\t{:?}\n", + i, key, iv, pt, ct, out + ); + } + } + + let data = include_bytes!(concat!("data/", $test_name, ".blb")); + for (i, row) in Blob4Iterator::new(data).unwrap().enumerate() { + let [key, iv, pt, ct] = row.unwrap(); + run_test(i, key, iv, pt, ct); + } + } + }; +} + +/// Define block mode decryption test +#[macro_export] +macro_rules! block_mode_dec_stealing_test { + ($name:ident, $test_name:expr, $cipher:ty $(,)?) => { + #[test] + fn $name() { + use cipher::{blobby::Blob4Iterator, KeyIvInit}; + + use xts::Decryptor; + + fn run_test(i: usize, key: &[u8], iv: &[u8], pt: &[u8], ct: &[u8]) { + assert_eq!(pt.len(), ct.len()); + // test block-by-block processing + + // MODIFICATION: We hardcode XTS encryptor here + let mut state = + as KeyIvInit>::new_from_slices(key, iv).unwrap(); + + let mut out = vec![0u8; ct.len()]; + + // MODIFICATION: We don't split the block and we call encrypt_directly + state.decrypt_b2b(ct, &mut out).unwrap(); + + // MODIFICATION: Crash here so we can get the actual output + if out != pt { + panic!( + "\n\ + Failed test №{}\n\ + key:\t{:?}\n\ + iv:\t{:?}\n\ + plaintext:\t{:?}\n\ + ciphertext:\t{:?}\n\ + actual_pt:\t{:?}\n", + i, key, iv, pt, ct, out + ); + } + } + + let data = include_bytes!(concat!("data/", $test_name, ".blb")); + for (i, row) in Blob4Iterator::new(data).unwrap().enumerate() { + let [key, iv, pt, ct] = row.unwrap(); + run_test(i, key, iv, pt, ct); + } + } + }; +} From 4c3f927ab541ab4153a6342f88a3a9b0596bf72f Mon Sep 17 00:00:00 2001 From: zer0x64 Date: Sun, 24 Nov 2024 16:19:40 -0500 Subject: [PATCH 15/23] [XTS] Code cleanup --- xts/src/decrypt.rs | 2 +- xts/src/encrypt.rs | 2 +- xts/src/xts_core.rs | 4 ---- 3 files changed, 2 insertions(+), 6 deletions(-) diff --git a/xts/src/decrypt.rs b/xts/src/decrypt.rs index 122e604..7df383d 100644 --- a/xts/src/decrypt.rs +++ b/xts/src/decrypt.rs @@ -90,7 +90,7 @@ where } /// Decrypt `inout` buffer. - pub fn decrypt_inout(&mut self, mut buf: InOutBuf<'_, '_, u8>) -> Result<()> { + pub fn decrypt_inout(&mut self, buf: InOutBuf<'_, '_, u8>) -> Result<()> { if buf.len() < BS::USIZE { return Err(Error); }; diff --git a/xts/src/encrypt.rs b/xts/src/encrypt.rs index 18ce7b5..9f7f288 100644 --- a/xts/src/encrypt.rs +++ b/xts/src/encrypt.rs @@ -89,7 +89,7 @@ where } /// Encrypt `inout` buffer. - pub fn encrypt_inout(&mut self, mut buf: InOutBuf<'_, '_, u8>) -> Result<()> { + pub fn encrypt_inout(&mut self, buf: InOutBuf<'_, '_, u8>) -> Result<()> { if buf.len() < BS::USIZE { return Err(Error); }; diff --git a/xts/src/xts_core.rs b/xts/src/xts_core.rs index 9e19a50..144b709 100644 --- a/xts/src/xts_core.rs +++ b/xts/src/xts_core.rs @@ -1,12 +1,8 @@ -use cipher::typenum::Unsigned; -use cipher::BlockCipherDecrypt; use cipher::{ crypto_common::BlockSizes, Array, Block, BlockCipherEncrypt, BlockSizeUser, InOut, InOutBuf, ParBlocks, ParBlocksSizeUser, }; -use crate::{Error, Result}; - use crate::gf::{gf_mul, gf_reverse_mul}; use crate::xor; From f98d604cb02790aa5eb3ff475d8a95271840a762 Mon Sep 17 00:00:00 2001 From: zer0x64 Date: Sun, 24 Nov 2024 16:44:29 -0500 Subject: [PATCH 16/23] [XTS] Fixed doctests --- xts/src/lib.rs | 48 ++++++++++++++++++++++++------------------------ 1 file changed, 24 insertions(+), 24 deletions(-) diff --git a/xts/src/lib.rs b/xts/src/lib.rs index cdb888e..d2e8aa7 100644 --- a/xts/src/lib.rs +++ b/xts/src/lib.rs @@ -11,45 +11,46 @@ //! //! # Example //! ``` -//! use aes::cipher::{BlockModeEncrypt, BlockModeDecrypt, KeyIvInit}; +//! use aes::cipher::KeyIvInit; //! use hex_literal::hex; //! //! type Aes128XtsEnc = xts::Encryptor; //! type Aes128XtsDec = xts::Decryptor; //! -//! let key = [0x42; 32]; -//! let tweak = [0x24; 16]; +//! let key = [0x42u8; 32]; +//! let mut tweak = [0x24u8; 16]; +//! tweak[8..].fill(0); +//! //! let plaintext = *b"hello world! this is my plaintext."; //! let ciphertext = hex!( // TODO fix this -//! "c7fe247ef97b21f07cbdd26cb5d346bf" -//! "d27867cb00d9486723e159978fb9a5f9" -//! "14cfb228a710de4171e396e7b6cf859e" +//! "bf970595626410ad91f032cc5fa36bcafb5cfe9c2bfe7e226582ec079a27e8c8521c" //! ); //! //! // encrypt/decrypt in-place -//! // buffer must be big enough for padded plaintext +//! // XTS does not need padding, so output is the same length as the buffer //! let pt_len = plaintext.len(); -//! let mut buf = [0u8; pt_len]; +//! let mut buf = vec![0u8; pt_len]; //! buf.copy_from_slice(&plaintext); +//! //! Aes128XtsEnc::new(&key.into(), &tweak.into()) -//! .encrypt_blocks(&mut buf) +//! .encrypt(&mut buf) //! .unwrap(); //! assert_eq!(&buf, &ciphertext); //! -//! Aes128XtsDec::new(&key.into(), &iv.into()) -//! .decrypt_blocks(&mut buf) +//! Aes128XtsDec::new(&key.into(), &tweak.into()) +//! .decrypt(&mut buf) //! .unwrap(); //! assert_eq!(&buf, &plaintext); //! //! // encrypt/decrypt from buffer to buffer -//! let mut buf = [0u8; pt_len]; -//! Aes128XtsEnc::new(&key.into(), &iv.into()) -//! .encrypt_blocks_b2b(&plaintext, &mut buf) +//! let mut buf = vec![0u8; pt_len]; +//! Aes128XtsEnc::new(&key.into(), &tweak.into()) +//! .encrypt_b2b(&plaintext, &mut buf) //! .unwrap(); //! assert_eq!(&buf, &ciphertext); //! -//! let pt = Aes128XtsDec::new(&key.into(), &iv.into()) -//! .decrypt_blocks_b2b(&ct, &mut buf) +//! Aes128XtsDec::new(&key.into(), &tweak.into()) +//! .decrypt_b2b(&ciphertext, &mut buf) //! .unwrap(); //! assert_eq!(&buf, &plaintext); //! ``` @@ -63,21 +64,20 @@ //! # type Aes128XtsEnc = xts::Encryptor; //! # type Aes128XtsDec = xts::Decryptor; //! # let key = [0x42; 32]; -//! # let iv = [0x24; 16]; +//! # let mut tweak = [0x24; 8]; +//! # tweak[8..].fill(0); +//! //! # let plaintext = *b"hello world! this is my plaintext."; //! # let ciphertext = hex!( -//! # "c7fe247ef97b21f07cbdd26cb5d346bf" -//! # "d27867cb00d9486723e159978fb9a5f9" -//! # "14cfb228a710de4171e396e7b6cf859e" +//! # "bf970595626410ad91f032cc5fa36bcafb5cfe9c2bfe7e226582ec079a27e8c8521c" //! # ); -//! // let res = Aes128CbcEnc::new(&key.into(), &iv.into()) +//! // let res = Aes128XtsEnc::new(&key.into(), &tweak.into()) //! // .encrypt_blocks_vec(&plaintext); //! // assert_eq!(res[..], ciphertext[..]); -//! // let res = Aes128CbcDec::new(&key.into(), &iv.into()) -//! // .decrypt_padded_vec::(&res) +//! // let res = Aes128XtsDec::new(&key.into(), &tweak.into()) +//! // .decrypt_blocks_vec::(&res) //! // .unwrap(); //! // assert_eq!(res[..], plaintext[..]); -//! # } //! ``` //! //! [1]: https://en.wikipedia.org/wiki/Disk_encryption_theory#XTS From 0ebe09f212802d7880b5c5b6e243d40f7f1ece1e Mon Sep 17 00:00:00 2001 From: zer0x64 Date: Sun, 24 Nov 2024 16:48:36 -0500 Subject: [PATCH 17/23] [XTS] Removed doc regarding std since it isn't implemented yet --- xts/src/lib.rs | 30 ++---------------------------- 1 file changed, 2 insertions(+), 28 deletions(-) diff --git a/xts/src/lib.rs b/xts/src/lib.rs index d2e8aa7..ba66afd 100644 --- a/xts/src/lib.rs +++ b/xts/src/lib.rs @@ -20,7 +20,7 @@ //! let key = [0x42u8; 32]; //! let mut tweak = [0x24u8; 16]; //! tweak[8..].fill(0); -//! +//! //! let plaintext = *b"hello world! this is my plaintext."; //! let ciphertext = hex!( // TODO fix this //! "bf970595626410ad91f032cc5fa36bcafb5cfe9c2bfe7e226582ec079a27e8c8521c" @@ -31,7 +31,7 @@ //! let pt_len = plaintext.len(); //! let mut buf = vec![0u8; pt_len]; //! buf.copy_from_slice(&plaintext); -//! +//! //! Aes128XtsEnc::new(&key.into(), &tweak.into()) //! .encrypt(&mut buf) //! .unwrap(); @@ -54,32 +54,6 @@ //! .unwrap(); //! assert_eq!(&buf, &plaintext); //! ``` -//! -//! With enabled `alloc` (or `std`) feature you also can use allocating -//! convenience methods: -//! NOTE FOR REVIEWER: Aren't we missing a `encrypt_blocks_vec` method or something in the cipher crate? -//! ``` -//! # use aes::cipher::{BlockModeEncrypt, BlockModeDecrypt, KeyIvInit}; -//! # use hex_literal::hex; -//! # type Aes128XtsEnc = xts::Encryptor; -//! # type Aes128XtsDec = xts::Decryptor; -//! # let key = [0x42; 32]; -//! # let mut tweak = [0x24; 8]; -//! # tweak[8..].fill(0); -//! -//! # let plaintext = *b"hello world! this is my plaintext."; -//! # let ciphertext = hex!( -//! # "bf970595626410ad91f032cc5fa36bcafb5cfe9c2bfe7e226582ec079a27e8c8521c" -//! # ); -//! // let res = Aes128XtsEnc::new(&key.into(), &tweak.into()) -//! // .encrypt_blocks_vec(&plaintext); -//! // assert_eq!(res[..], ciphertext[..]); -//! // let res = Aes128XtsDec::new(&key.into(), &tweak.into()) -//! // .decrypt_blocks_vec::(&res) -//! // .unwrap(); -//! // assert_eq!(res[..], plaintext[..]); -//! ``` -//! //! [1]: https://en.wikipedia.org/wiki/Disk_encryption_theory#XTS #![no_std] From 61a4a061580e49c7d9d7a219d14ee56d8d0757b4 Mon Sep 17 00:00:00 2001 From: zer0x64 Date: Sun, 24 Nov 2024 16:58:28 -0500 Subject: [PATCH 18/23] [XTS] Update docs --- xts/src/decrypt.rs | 22 +++++++++++----------- xts/src/encrypt.rs | 20 ++++++++++---------- xts/src/lib.rs | 17 +++-------------- 3 files changed, 24 insertions(+), 35 deletions(-) diff --git a/xts/src/decrypt.rs b/xts/src/decrypt.rs index 7df383d..a38971f 100644 --- a/xts/src/decrypt.rs +++ b/xts/src/decrypt.rs @@ -15,20 +15,20 @@ use cipher::{ #[cfg(feature = "zeroize")] use cipher::zeroize::{Zeroize, ZeroizeOnDrop}; -/// XTS mode decryptor -pub type Decryptor = SplitDecryptor; +/// XTS mode decryptor. +pub type Decryptor = SplitDecryptor; /// XTS mode decryptor. -/// This structure allows using different cipher for the cipher itself and the tweak. +/// This structure allows using different ciphers for the cipher itself and the tweak. #[derive(Clone)] -pub struct SplitDecryptor +pub struct SplitDecryptor where - C: BlockCipherDecrypt, - T: BlockCipherEncrypt, + Cipher: BlockCipherDecrypt, + Tweaker: BlockCipherEncrypt, { - cipher: C, - tweaker: T, - iv: Block, + cipher: Cipher, + tweaker: Tweaker, + iv: Block, } impl KeySizeUser for SplitDecryptor @@ -71,7 +71,7 @@ where C: BlockCipherDecrypt + KeyInit + KeySizeUser, T: BlockCipherEncrypt + KeyInit + KeySizeUser, { - /// Create an XTS array and precompute it + /// Create an XTS context and precompute the tweak. pub fn new_from_split_keys(cipher_key: &Key, tweak_key: &Key, iv: &Block) -> Self { let cipher = C::new(&cipher_key); let tweaker = T::new(&tweak_key); @@ -84,7 +84,7 @@ where } } - /// Change the IV/sector number + /// Change the IV/sector number. pub fn reset_iv(&mut self, iv: &Block) { self.iv = precompute_iv(&self.tweaker, iv) } diff --git a/xts/src/encrypt.rs b/xts/src/encrypt.rs index 9f7f288..4f74e4c 100644 --- a/xts/src/encrypt.rs +++ b/xts/src/encrypt.rs @@ -14,19 +14,19 @@ use core::{fmt, ops::Add}; use cipher::zeroize::{Zeroize, ZeroizeOnDrop}; /// XTS mode encryptor. -pub type Encryptor = SplitEncryptor; +pub type Encryptor = SplitEncryptor; /// XTS mode encryptor. -/// This structure allows using different cipher for the cipher itself and the tweak. +/// This structure allows using different ciphers for the cipher itself and the tweak. #[derive(Clone)] -pub struct SplitEncryptor +pub struct SplitEncryptor where - C: BlockCipherEncrypt, - T: BlockCipherEncrypt, + Cipher: BlockCipherEncrypt, + Tweaker: BlockCipherEncrypt, { - cipher: C, - tweaker: T, - iv: Block, + cipher: Cipher, + tweaker: Tweaker, + iv: Block, } // This would probably be the cleanest way to do it, but it would require a way to multiply a typenum by 2 @@ -70,7 +70,7 @@ where C: BlockCipherEncrypt + KeyInit + KeySizeUser, T: BlockCipherEncrypt + KeyInit + KeySizeUser, { - /// Create an XTS array and precompute it + /// Create an XTS context and precompute the tweak. pub fn new_from_split_keys(k1: &Key, k2: &Key, iv: &Block) -> Self { let cipher = C::new(&k1); let tweaker = T::new(&k2); @@ -83,7 +83,7 @@ where } } - /// Change the IV/sector number + /// Change the IV/sector number. pub fn reset_iv(&mut self, iv: &Block) { self.iv = precompute_iv(&self.tweaker, iv) } diff --git a/xts/src/lib.rs b/xts/src/lib.rs index ba66afd..0ae60f2 100644 --- a/xts/src/lib.rs +++ b/xts/src/lib.rs @@ -6,7 +6,7 @@ //! //! This crate does not ensure ciphertexts are authentic! Thus ciphertext integrity //! is not verified, which can lead to serious vulnerabilities! -//! [AEADs][https://github.com/RustCrypto/AEADs] provide simple authenticated encryption, +//! [AEADs](https://github.com/RustCrypto/AEADs) provide simple authenticated encryption, //! which is much less error-prone than manual integrity verification. //! //! # Example @@ -72,6 +72,7 @@ mod encrypt; mod gf; mod xts_core; +pub use cipher; pub use decrypt::{Decryptor, SplitDecryptor}; pub use encrypt::{Encryptor, SplitEncryptor}; @@ -79,21 +80,9 @@ pub use encrypt::{Encryptor, SplitEncryptor}; #[derive(Copy, Clone, Debug)] pub struct Error; -/// Result type of the crate +/// Result type of the crate. pub type Result = core::result::Result; -/// Encryption functionality of XTS modes. -pub trait Encrypt: Sized { - /// Encrypt buffer in place - fn encrypt(self, buf: &mut [u8]) -> Result<()>; -} - -/// Decryption functionality of XTS modes. -pub trait Decypt: Sized { - /// Decrypt buffer in place - fn decrypt(self, buf: &mut [u8]) -> Result<()>; -} - #[inline(always)] fn xor(out: &mut Array, buf: &Array) { for (a, b) in out.iter_mut().zip(buf) { From fbe2c714ae66daa64ef71bd8d3d2925e04fd6801 Mon Sep 17 00:00:00 2001 From: zer0x64 Date: Sun, 24 Nov 2024 17:02:36 -0500 Subject: [PATCH 19/23] [XTS] Reverse opt-leve changes done for debugging --- Cargo.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 20fd564..432a71c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -2,5 +2,5 @@ resolver = "2" members = ["belt-ctr", "cbc", "cts", "cfb8", "cfb-mode", "ctr", "ige", "ofb", "pcbc", "xts"] -#[profile.dev] -#opt-level = 2 +[profile.dev] +opt-level = 2 From 82065aca53fc723f3143df6aa1c7647f6dd01fcd Mon Sep 17 00:00:00 2001 From: zer0x64 Date: Sun, 24 Nov 2024 18:15:58 -0500 Subject: [PATCH 20/23] [XTS] Update global README --- README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/README.md b/README.md index 3a365da..cf4f881 100644 --- a/README.md +++ b/README.md @@ -31,6 +31,7 @@ less error-prone than manual integrity verification. | [Infinite Garble Extension][IGE] | [`ige`] | [![crates.io](https://img.shields.io/crates/v/ige.svg)](https://crates.io/crates/ige) | [![Documentation](https://docs.rs/ige/badge.svg)](https://docs.rs/ige) | ![MSRV 1.81][msrv-1.81] | | [Output Feedback][OFB] | [`ofb`] | [![crates.io](https://img.shields.io/crates/v/ofb.svg)](https://crates.io/crates/ofb) | [![Documentation](https://docs.rs/ofb/badge.svg)](https://docs.rs/ofb) | ![MSRV 1.81][msrv-1.81] | | [Propagating Cipher Block Chaining][PCBC] | [`pcbc`] | [![crates.io](https://img.shields.io/crates/v/pcbc.svg)](https://crates.io/crates/pcbc) | [![Documentation](https://docs.rs/pcbc/badge.svg)](https://docs.rs/pcbc) | ![MSRV 1.81][msrv-1.81] | +| [Xor-Encrypt-Xor Tweaked-codebook with ciphertext Stealing][XTS] | [`xts`] | [![crates.io](https://img.shields.io/crates/v/xts.svg)](https://crates.io/crates/xts) | [![Documentation](https://docs.rs/xts/badge.svg)](https://docs.rs/xts) | ![MSRV 1.81][msrv-1.81] | ### Minimum Supported Rust Version (MSRV) Policy @@ -73,6 +74,7 @@ Unless you explicitly state otherwise, any contribution intentionally submitted [`ige`]: ./ige [`ofb`]: ./ofb [`pcbc`]: ./pcbc +[`xts`]: ./xts [//]: # (links) @@ -87,3 +89,4 @@ Unless you explicitly state otherwise, any contribution intentionally submitted [IGE]: https://www.links.org/files/openssl-ige.pdf [OFB]: https://en.wikipedia.org/wiki/Block_cipher_mode_of_operation#Output_feedback_(OFB) [PCBC]: https://en.wikipedia.org/wiki/Block_cipher_mode_of_operation#Propagating_cipher_block_chaining_(PCBC) +[XTS]: https://en.wikipedia.org/wiki/Disk_encryption_theory#XTS From 41c61bed8d2e16b460a97919207302e0a04c1f27 Mon Sep 17 00:00:00 2001 From: zer0x64 Date: Wed, 27 Nov 2024 17:07:07 -0500 Subject: [PATCH 21/23] [XTS] Made GF operations constant time --- xts/src/gf.rs | 27 ++++++++++----------------- 1 file changed, 10 insertions(+), 17 deletions(-) diff --git a/xts/src/gf.rs b/xts/src/gf.rs index c2caa50..4443a21 100644 --- a/xts/src/gf.rs +++ b/xts/src/gf.rs @@ -3,7 +3,7 @@ use cipher::{crypto_common::BlockSizes, Array}; /// Derived from the polynomial x^128 + x^5 + x + 1 const GF_MOD: u8 = 0x87; -pub fn gf_mul(tweak: &mut Array) -> bool +pub fn gf_mul(tweak: &mut Array) -> u8 where BS: BlockSizes, { @@ -24,21 +24,17 @@ where } // If there is a carry, we mod by the polynomial - if carry == 1 { - tweak[0] ^= GF_MOD; - } + tweak[0] ^= 0u8.wrapping_sub(carry) & GF_MOD; - carry == 1 + carry } // This is only used once when decrypting with ciphertext stealing -pub fn gf_reverse_mul(tweak: &mut Array, carry: bool) +pub fn gf_reverse_mul(tweak: &mut Array, carry: u8) where BS: BlockSizes, { - if carry { - tweak[0] ^= GF_MOD; - } + tweak[0] ^= 0u8.wrapping_sub(carry) & GF_MOD; let mut new_carry = 0; @@ -57,10 +53,7 @@ where } // If there is a carry, we mod by the polynomial - if carry { - let len = tweak.len(); - tweak[len - 1] |= 0x80; - } + *tweak.last_mut().expect("tweak should never be empty") |= carry << 7; } #[cfg(test)] @@ -76,7 +69,7 @@ mod tests { let carry = gf_mul(&mut input); - assert!(!carry); + assert_eq!(carry, 0); assert_eq!(input, expected_output); } @@ -88,7 +81,7 @@ mod tests { let carry = gf_mul(&mut input); - assert!(carry); + assert_eq!(carry, 1); assert_eq!(input, expected_output) } @@ -97,7 +90,7 @@ mod tests { let mut input = Array::::try_from([0xAA; 16]).unwrap(); let expected_output = [0x55; 16]; - gf_reverse_mul(&mut input, false); + gf_reverse_mul(&mut input, 0); assert_eq!(input, expected_output); } @@ -109,7 +102,7 @@ mod tests { expected_output[0] = 0x16; expected_output[15] = 0xd5; - gf_reverse_mul(&mut input, true); + gf_reverse_mul(&mut input, 1); assert_eq!(input, expected_output); } From 661e998355f76ef9a680d599f8410a40f12624e0 Mon Sep 17 00:00:00 2001 From: zer0x64 Date: Wed, 27 Nov 2024 17:08:36 -0500 Subject: [PATCH 22/23] [XTS] clippy --- xts/src/decrypt.rs | 6 +++--- xts/src/encrypt.rs | 8 ++++---- xts/src/gf.rs | 8 ++++---- xts/src/xts_core.rs | 8 ++++---- 4 files changed, 15 insertions(+), 15 deletions(-) diff --git a/xts/src/decrypt.rs b/xts/src/decrypt.rs index a38971f..68fd56f 100644 --- a/xts/src/decrypt.rs +++ b/xts/src/decrypt.rs @@ -73,9 +73,9 @@ where { /// Create an XTS context and precompute the tweak. pub fn new_from_split_keys(cipher_key: &Key, tweak_key: &Key, iv: &Block) -> Self { - let cipher = C::new(&cipher_key); - let tweaker = T::new(&tweak_key); - let iv = precompute_iv(&tweaker, &iv); + let cipher = C::new(cipher_key); + let tweaker = T::new(tweak_key); + let iv = precompute_iv(&tweaker, iv); Self { cipher, diff --git a/xts/src/encrypt.rs b/xts/src/encrypt.rs index 4f74e4c..6bf1466 100644 --- a/xts/src/encrypt.rs +++ b/xts/src/encrypt.rs @@ -72,9 +72,9 @@ where { /// Create an XTS context and precompute the tweak. pub fn new_from_split_keys(k1: &Key, k2: &Key, iv: &Block) -> Self { - let cipher = C::new(&k1); - let tweaker = T::new(&k2); - let iv = precompute_iv(&tweaker, &iv); + let cipher = C::new(k1); + let tweaker = T::new(k2); + let iv = precompute_iv(&tweaker, iv); Self { cipher, @@ -345,6 +345,6 @@ where } fn get_iv_mut(&mut self) -> &mut Block { - &mut self.iv + self.iv } } diff --git a/xts/src/gf.rs b/xts/src/gf.rs index 4443a21..70c7431 100644 --- a/xts/src/gf.rs +++ b/xts/src/gf.rs @@ -64,7 +64,7 @@ mod tests { #[test] fn test_gf_mul() { - let mut input = Array::::try_from([0x55; 16]).unwrap(); + let mut input = Array::::from([0x55; 16]); let expected_output = [0xAA; 16]; let carry = gf_mul(&mut input); @@ -75,7 +75,7 @@ mod tests { #[test] fn test_gf_mul_overflow() { - let mut input = Array::::try_from([0xAA; 16]).unwrap(); + let mut input = Array::::from([0xAA; 16]); let mut expected_output = [0x55; 16]; expected_output[0] = 0xd3; @@ -87,7 +87,7 @@ mod tests { #[test] fn test_gf_reverse_mul() { - let mut input = Array::::try_from([0xAA; 16]).unwrap(); + let mut input = Array::::from([0xAA; 16]); let expected_output = [0x55; 16]; gf_reverse_mul(&mut input, 0); @@ -97,7 +97,7 @@ mod tests { #[test] fn test_gf_reverse_mul_overflow() { - let mut input = Array::::try_from([0xAA; 16]).unwrap(); + let mut input = Array::::from([0xAA; 16]); let mut expected_output = [0x55; 16]; expected_output[0] = 0x16; expected_output[15] = 0xd5; diff --git a/xts/src/xts_core.rs b/xts/src/xts_core.rs index 144b709..a093b4f 100644 --- a/xts/src/xts_core.rs +++ b/xts/src/xts_core.rs @@ -130,9 +130,9 @@ pub(crate) trait Stealer: BlockSizeUser { fn is_decrypt() -> bool; fn process_xts_block(&mut self, block: &mut Block) { - xor(block, &self.get_iv()); + xor(block, self.get_iv()); self.process_block(block); - xor(block, &self.get_iv()); + xor(block, self.get_iv()); let iv = self.get_iv_mut(); gf_mul(iv); @@ -152,8 +152,8 @@ pub(crate) trait Stealer: BlockSizeUser { if Self::is_decrypt() { // We fast forward the multiplication here let carry = { - let mut iv = self.get_iv_mut(); - let carry = gf_mul(&mut iv); + let iv = self.get_iv_mut(); + let carry = gf_mul(iv); xor(second_to_last_block, iv); From d47b5dc788298da5a2b7f912489539be8dc4c9c4 Mon Sep 17 00:00:00 2001 From: zer0x64 Date: Wed, 27 Nov 2024 17:13:43 -0500 Subject: [PATCH 23/23] [XTS] add workflow --- .github/workflows/xts.yaml | 60 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 60 insertions(+) create mode 100644 .github/workflows/xts.yaml diff --git a/.github/workflows/xts.yaml b/.github/workflows/xts.yaml new file mode 100644 index 0000000..3146519 --- /dev/null +++ b/.github/workflows/xts.yaml @@ -0,0 +1,60 @@ +name: xts + +on: + pull_request: + paths: + - "xts/**" + - "Cargo.*" + push: + branches: [master] + +defaults: + run: + working-directory: xts + +env: + CARGO_INCREMENTAL: 0 + RUSTFLAGS: "-Dwarnings" + +jobs: + build: + runs-on: ubuntu-latest + strategy: + matrix: + rust: + - 1.81.0 # MSRV + - stable + target: + - thumbv7em-none-eabi + - wasm32-unknown-unknown + steps: + - uses: actions/checkout@v4 + - uses: RustCrypto/actions/cargo-cache@master + - uses: dtolnay/rust-toolchain@master + with: + toolchain: ${{ matrix.rust }} + targets: ${{ matrix.target }} + - run: cargo build --no-default-features --release --target ${{ matrix.target }} + + minimal-versions: + # disabled until belt-block gets published + if: false + uses: RustCrypto/actions/.github/workflows/minimal-versions.yml@master + with: + working-directory: ${{ github.workflow }} + + test: + runs-on: ubuntu-latest + strategy: + matrix: + rust: + - 1.81.0 # MSRV + - stable + steps: + - uses: actions/checkout@v4 + - uses: RustCrypto/actions/cargo-cache@master + - uses: dtolnay/rust-toolchain@master + with: + toolchain: ${{ matrix.rust }} + - run: cargo test + - run: cargo test --all-features