From e974e2dcfa7dae0b71f5c16c53cdf1c0731ebe52 Mon Sep 17 00:00:00 2001 From: Alexandr Kitaev Date: Thu, 9 Nov 2023 12:48:59 +0300 Subject: [PATCH 01/10] Implement belt-kwp --- Cargo.lock | 17 +++ Cargo.toml | 1 + belt-kwp/LICENSE-APACHE | 201 ++++++++++++++++++++++++++++++++++++ belt-kwp/LICENSE-MIT | 25 +++++ belt-kwp/README.md | 76 ++++++++++++++ belt-kwp/src/error.rs | 49 +++++++++ belt-kwp/tests/kwp_tests.rs | 43 ++++++++ 7 files changed, 412 insertions(+) create mode 100644 belt-kwp/LICENSE-APACHE create mode 100644 belt-kwp/LICENSE-MIT create mode 100644 belt-kwp/README.md create mode 100644 belt-kwp/src/error.rs create mode 100644 belt-kwp/tests/kwp_tests.rs diff --git a/Cargo.lock b/Cargo.lock index 437abe2..217d170 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -22,6 +22,23 @@ dependencies = [ "hex-literal", ] +[[package]] +name = "belt-block" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9aa1eef3994e2ccd304a78fe3fea4a73e5792007f85f09b79bb82143ca5f82b" +dependencies = [ + "cipher", +] + +[[package]] +name = "belt-kwp" +version = "0.0.0" +dependencies = [ + "belt-block", + "hex-literal", +] + [[package]] name = "cfg-if" version = "1.0.0" diff --git a/Cargo.toml b/Cargo.toml index a25ce31..429f12e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,7 @@ [workspace] members = [ "aes-kw", + "belt-kwp", ] [profile.dev] diff --git a/belt-kwp/LICENSE-APACHE b/belt-kwp/LICENSE-APACHE new file mode 100644 index 0000000..53b7ccd --- /dev/null +++ b/belt-kwp/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 [yyyy] [name of copyright owner] + +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. \ No newline at end of file diff --git a/belt-kwp/LICENSE-MIT b/belt-kwp/LICENSE-MIT new file mode 100644 index 0000000..a2b36f2 --- /dev/null +++ b/belt-kwp/LICENSE-MIT @@ -0,0 +1,25 @@ +Copyright (c) 2021-2022 RustCrypto Developers + +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/belt-kwp/README.md b/belt-kwp/README.md new file mode 100644 index 0000000..a7a052d --- /dev/null +++ b/belt-kwp/README.md @@ -0,0 +1,76 @@ +# RustCrypto: BelT Key Wrap Algorithm + +[![crate][crate-image]][crate-link] +[![Docs][docs-image]][docs-link] +![Apache2/MIT licensed][license-image] +![Rust Version][rustc-image] +[![Build Status][build-image]][build-link] + +Pure Rust implementation of the [BelT Key Wrap]. + +# Usage + +The most common way to use BelT-KWP is as follows: you provide the Key Wrapping Key and the key-to-be-wrapped, then wrap it, or provide a wrapped-key and unwrap it. + +```rust +# fn main() -> Result<(), Box> { +# #[cfg(feature = "std")] +# { +use hex_literal::hex; +use belt_kwp::BeltKwp; + +let x = hex!("B194BAC8 0A08F53B 366D008E 584A5DE4 8504FA9D 1BB6C7AC 252E72C2 02FDCE0D"); +let i = hex!("5BE3D612 17B96181 FE6786AD 716B890B"); +let k = hex!("E9DEE72C 8F0C0FA6 2DDB49F4 6F739647 06075316 ED247A37 39CBA383 03A98BF6"); +let y = hex!("49A38EE1 08D6C742 E52B774F 00A6EF98 B106CBD1 3EA4FB06 80323051 BC04DF76 E487B055 C69BCF54 1176169F 1DC9F6C8"); + +let mut wrapped = [0u8; 48]; +let mut unwrapped = [0u8; 48]; + +let kek = BeltKwp::new(&k.into()); + +kek.wrap_key(&x, &i, &mut wrapped).unwrap(); +assert_eq!(y, wrapped); + +kek.unwrap_key(&y, &i, &mut unwrapped).unwrap(); +assert_eq!(x, unwrapped[..32]); +# } +# Ok(()) +# } +``` + +## Minimum Supported Rust Version + +This crate requires **Rust 1.56** at a minimum. + +We may change the MSRV in the future, but it will be accompanied by a minor +version bump. + +## 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/bel-kwp.svg +[crate-link]: https://crates.io/crates/belt-kwp +[docs-image]: https://docs.rs/belt-kwp/badge.svg +[docs-link]: https://docs.rs/belt-kwp/ +[license-image]: https://img.shields.io/badge/license-Apache2.0/MIT-blue.svg +[rustc-image]: https://img.shields.io/badge/rustc-1.56+-blue.svg +[build-image]: https://github.com/RustCrypto/key-wraps/actions/workflows/belt-kwp.yml/badge.svg +[build-link]: https://github.com/RustCrypto/key-wraps/actions/workflows/belt-kwp.yml + +[//]: # (links) +[BelT Key Wrap]: https://apmi.bsu.by/assets/files/std/belt-spec371.pdf diff --git a/belt-kwp/src/error.rs b/belt-kwp/src/error.rs new file mode 100644 index 0000000..4f1b993 --- /dev/null +++ b/belt-kwp/src/error.rs @@ -0,0 +1,49 @@ +use core::fmt; + +/// Result type with the `belt-kwp` crate's [`Error`]. +pub type Result = core::result::Result; + +/// Errors emitted from the wrap and unwrap operations. +#[derive(Debug)] +pub enum Error { + /// Input data length invalid. + InvalidDataSize, + + /// Invalid KEK size. + InvalidKekSize { + /// KEK size provided in bytes (expected 32). + size: usize, + }, + + /// Output buffer size invalid. + InvalidOutputSize { + /// Expected size in bytes. + expected: usize, + }, + + /// Integrity check did not pass. + IntegrityCheckFailed, +} + +impl fmt::Display for Error { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Error::InvalidDataSize => write!( + f, + "data must be a multiple of 64 bits and more than 16 bytes" + ), + Error::InvalidOutputSize { expected } => { + write!(f, "invalid output buffer size: expected {}", expected) + } + Error::IntegrityCheckFailed => { + write!(f, "integrity check failed") + } + Error::InvalidKekSize { size } => { + write!(f, "invalid KEK size: {}", size) + } + } + } +} + +#[cfg(feature = "std")] +impl std::error::Error for Error {} diff --git a/belt-kwp/tests/kwp_tests.rs b/belt-kwp/tests/kwp_tests.rs new file mode 100644 index 0000000..b5bb3c6 --- /dev/null +++ b/belt-kwp/tests/kwp_tests.rs @@ -0,0 +1,43 @@ +#[cfg(test)] +mod tests { + use belt_kwp::BeltKwp; + use hex_literal::hex; + + #[test] + fn test_key_wrap() { + let x = hex!("B194BAC8 0A08F53B 366D008E 584A5DE4 8504FA9D 1BB6C7AC 252E72C2 02FDCE0D"); + let i = hex!("5BE3D612 17B96181 FE6786AD 716B890B"); + let k = hex!("E9DEE72C 8F0C0FA6 2DDB49F4 6F739647 06075316 ED247A37 39CBA383 03A98BF6"); + let y = hex!("49A38EE1 08D6C742 E52B774F 00A6EF98 B106CBD1 3EA4FB06 80323051 BC04DF76 E487B055 C69BCF54 1176169F 1DC9F6C8"); + + let mut wrapped = [0u8; 48]; + let mut unwrapped = [0u8; 48]; + + let kek = BeltKwp::new(&k.into()); + + kek.wrap_key(&x, &i, &mut wrapped).unwrap(); + assert_eq!(y, wrapped); + + kek.unwrap_key(&y, &i, &mut unwrapped).unwrap(); + assert_eq!(x, unwrapped[..32]); + } + + #[test] + fn test_key_unwrap() { + let y = hex!("49A38EE1 08D6C742 E52B774F 00A6EF98 B106CBD1 3EA4FB06 80323051 BC04DF76 E487B055 C69BCF54 1176169F 1DC9F6C8"); + let i = hex!("5BE3D612 17B96181 FE6786AD 716B890B"); + let k = hex!("E9DEE72C 8F0C0FA6 2DDB49F4 6F739647 06075316 ED247A37 39CBA383 03A98BF6"); + let x = hex!("B194BAC8 0A08F53B 366D008E 584A5DE4 8504FA9D 1BB6C7AC 252E72C2 02FDCE0D"); + + let mut unwrapped = [0u8; 48]; + let mut wrapped = [0u8; 48]; + + let kek = BeltKwp::new(&k.into()); + + kek.unwrap_key(&y, &i, &mut unwrapped).unwrap(); + assert_eq!(x, unwrapped[..32]); + + kek.wrap_key(&x, &i, &mut wrapped).unwrap(); + assert_eq!(y, wrapped); + } +} From 33f29134d8216e8c8a363cb1e62f3aee0a79b036 Mon Sep 17 00:00:00 2001 From: Alexandr Kitaev Date: Thu, 9 Nov 2023 12:56:03 +0300 Subject: [PATCH 02/10] fix/forgot to commit files --- belt-kwp/Cargo.toml | 27 +++++++++ belt-kwp/src/lib.rs | 137 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 164 insertions(+) create mode 100644 belt-kwp/Cargo.toml create mode 100644 belt-kwp/src/lib.rs diff --git a/belt-kwp/Cargo.toml b/belt-kwp/Cargo.toml new file mode 100644 index 0000000..a7dfeef --- /dev/null +++ b/belt-kwp/Cargo.toml @@ -0,0 +1,27 @@ +[package] +name = "belt-kwp" +version = "0.0.0" +description = "STB 34.101.30-2020 Key Wrap Algorithm (KWP) implementation using Belt block cipher" +authors = ["RustCrypto Developers"] +license = "MIT OR Apache-2.0" +homepage = "https://github.com/RustCrypto/key-wraps/" +repository = "https://github.com/RustCrypto/key-wraps/tree/belt-kwp" +keywords = ["crypto", "BELT-KW", "KW", "BELT-KWP", "KWP"] +categories = ["cryptography", "no-std"] +readme = "README.md" +edition = "2021" +rust-version = "1.56" + +[dependencies] +belt-block = "0.1" + +[dev-dependencies] +hex-literal = "0.3" + +[features] +alloc = [] +std = ["alloc"] + +[package.metadata.docs.rs] +all-features = true +rustdoc-args = ["--cfg", "docsrs"] \ No newline at end of file diff --git a/belt-kwp/src/lib.rs b/belt-kwp/src/lib.rs new file mode 100644 index 0000000..af7331f --- /dev/null +++ b/belt-kwp/src/lib.rs @@ -0,0 +1,137 @@ +#![no_std] +#![doc = include_str!("../README.md")] +#![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" +)] +#![cfg_attr(docsrs, feature(doc_cfg))] +#![forbid(unsafe_code)] +#![warn(missing_docs, rust_2018_idioms)] + +mod error; + +pub use error::{Error, Result}; + +#[cfg(feature = "alloc")] +#[macro_use] +extern crate alloc; +#[cfg(feature = "std")] +extern crate std; + +#[cfg(feature = "alloc")] +use alloc::vec::Vec; +use belt_block::cipher::consts::U32; +use belt_block::cipher::generic_array::GenericArray; +use belt_block::{belt_wblock_dec, belt_wblock_enc, to_u32}; + +/// Block size for BelT-KWP +pub const SEMIBLOCK_LEN: usize = 8; + +/// Size of an BelT-block "semiblock" in bytes. +pub const IV_LEN: usize = 16; + +impl From> for BeltKwp { + fn from(kek: GenericArray) -> Self { + BeltKwp::new(&kek) + } +} + +impl From<[u8; 32]> for BeltKwp { + fn from(kek: [u8; 32]) -> Self { + BeltKwp::new(&kek.into()) + } +} + +impl TryFrom<&[u8]> for BeltKwp { + type Error = Error; + + fn try_from(value: &[u8]) -> Result { + if value.len() == 32 { + Ok(BeltKwp::new(value.into())) + } else { + Err(Error::InvalidKekSize { size: value.len() }) + } + } +} + +/// A Key-Encrypting-Key (KEK) that can be used to wrap and unwrap other +/// keys. +#[derive(Debug, Clone, Copy, PartialEq)] +pub struct BeltKwp { + /// Initialized key + key: [u32; 8], +} + +impl BeltKwp { + /// Constructs a new Kek based on the appropriate raw key material. + pub fn new(key: &GenericArray) -> Self { + Self { + key: to_u32::<8>(key), + } + } + + /// BelT Key Wrap, as defined in STB 34.101.34-2020. + /// + /// The `out` buffer will be overwritten, and must be exactly [`IV_LEN`] + /// bytes (i.e. 16 bytes) longer than the length of `data`. + pub fn wrap_key(&self, x: &[u8], iv: &[u8], out: &mut [u8]) -> Result<()> { + if x.len() % SEMIBLOCK_LEN != 0 || x.len() < 32 || iv.len() != IV_LEN { + return Err(Error::InvalidDataSize); + } + + if out.len() != (x.len() + IV_LEN) { + return Err(Error::InvalidOutputSize { + expected: x.len() + IV_LEN, + }); + } + + out[..x.len()].copy_from_slice(x); + out[x.len()..].copy_from_slice(iv); + + // 1. Y ← belt-wblock(X || I, K) + belt_wblock_enc(out, &self.key).map_err(|_| Error::InvalidDataSize) + } + + /// BelT Key Unwrap, as defined in STB 34.101.31-2020. + /// + /// The `out` buffer will be overwritten, and it length must be exactly the length of `data`. + pub fn unwrap_key(&self, y: &[u8], iv: &[u8], out: &mut [u8]) -> Result<()> { + // 1. If |Y| mod 8 ≠ 0 or |Y| < 32 or |I| ≠ 16 then return error + if y.len() % SEMIBLOCK_LEN != 0 || y.len() < 32 || iv.len() != IV_LEN { + return Err(Error::InvalidDataSize); + } + + if out.len() != y.len() { + return Err(Error::InvalidOutputSize { expected: y.len() }); + } + + out.copy_from_slice(y); + + // 2. (X || r) ← belt-wblock^(-1)(Y, K) + belt_wblock_dec(out, &self.key).map_err(|_| Error::InvalidDataSize)?; + // 3. If r ≠ I then return error + if &out[y.len() - IV_LEN..] != iv { + return Err(Error::IntegrityCheckFailed); + } + + Ok(()) + } + + /// Computes [`Self::wrap`], allocating a [`Vec`] for the return value. + #[cfg(feature = "alloc")] + #[cfg_attr(docsrs, doc(cfg(feature = "alloc")))] + pub fn wrap_vec(&self, data: &[u8], iv: &[u8]) -> Result> { + let mut out = vec![0u8; data.len() + IV_LEN]; + self.wrap_key(data, iv, &mut out)?; + Ok(out) + } + + /// Computes [`Self::unwrap`], allocating a [`Vec`] for the return value. + #[cfg(feature = "alloc")] + #[cfg_attr(docsrs, doc(cfg(feature = "alloc")))] + pub fn unwrap_vec(&self, data: &[u8], iv: &[u8]) -> Result> { + let mut out = vec![0u8; data.len()]; + self.unwrap_key(data, iv, &mut out)?; + Ok(out) + } +} From 9b9b19a8357e82ee2329b0a88b71772f667e8b51 Mon Sep 17 00:00:00 2001 From: Alexandr Kitaev Date: Thu, 9 Nov 2023 12:57:46 +0300 Subject: [PATCH 03/10] fix/add github workflow --- .github/workflows/belt-kwp.yml | 64 ++++++++++++++++++++++++++++++++++ 1 file changed, 64 insertions(+) create mode 100644 .github/workflows/belt-kwp.yml diff --git a/.github/workflows/belt-kwp.yml b/.github/workflows/belt-kwp.yml new file mode 100644 index 0000000..8e18fc3 --- /dev/null +++ b/.github/workflows/belt-kwp.yml @@ -0,0 +1,64 @@ +name: belt-kwp + +on: + pull_request: + paths: + - "belt-kwp/**" + - "Cargo.*" + push: + branches: master + +defaults: + run: + working-directory: belt-kwp + +env: + CARGO_INCREMENTAL: 0 + RUSTFLAGS: "-Dwarnings" + +jobs: + build: + runs-on: ubuntu-latest + strategy: + matrix: + rust: + - 1.56.0 # MSRV + - stable + target: + - thumbv7em-none-eabi + - wasm32-unknown-unknown + steps: + - uses: actions/checkout@v4 + - uses: RustCrypto/actions/cargo-cache@master + - uses: actions-rs/toolchain@v1 + with: + profile: minimal + toolchain: ${{ matrix.rust }} + target: ${{ matrix.target }} + override: true + - run: cargo build --no-default-features --target ${{ matrix.target }} + + minimal-versions: + uses: RustCrypto/actions/.github/workflows/minimal-versions.yml@master + with: + working-directory: ${{ github.workflow }} + + test: + runs-on: ubuntu-latest + strategy: + matrix: + rust: + - 1.56.0 # MSRV + - stable + steps: + - uses: actions/checkout@v4 + - uses: RustCrypto/actions/cargo-cache@master + - uses: actions-rs/toolchain@v1 + with: + profile: minimal + toolchain: ${{ matrix.rust }} + override: true + - run: cargo check --all-features + - run: cargo test --no-default-features + - run: cargo test + - run: cargo test --all-features From 974b42833d5114a30f33e566818bfc75d0371f22 Mon Sep 17 00:00:00 2001 From: Alexandr Kitaev Date: Thu, 9 Nov 2023 13:14:31 +0300 Subject: [PATCH 04/10] fix/format use --- belt-kwp/src/lib.rs | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/belt-kwp/src/lib.rs b/belt-kwp/src/lib.rs index af7331f..0814947 100644 --- a/belt-kwp/src/lib.rs +++ b/belt-kwp/src/lib.rs @@ -1,8 +1,8 @@ #![no_std] #![doc = include_str!("../README.md")] #![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" +html_logo_url = "https://raw.githubusercontent.com/RustCrypto/media/6ee8e381/logo.svg", +html_favicon_url = "https://raw.githubusercontent.com/RustCrypto/media/6ee8e381/logo.svg" )] #![cfg_attr(docsrs, feature(doc_cfg))] #![forbid(unsafe_code)] @@ -20,9 +20,10 @@ extern crate std; #[cfg(feature = "alloc")] use alloc::vec::Vec; -use belt_block::cipher::consts::U32; -use belt_block::cipher::generic_array::GenericArray; -use belt_block::{belt_wblock_dec, belt_wblock_enc, to_u32}; +use belt_block::{ + cipher::{consts::U32, generic_array::GenericArray}, + to_u32, belt_wblock_dec, belt_wblock_enc, +}; /// Block size for BelT-KWP pub const SEMIBLOCK_LEN: usize = 8; From a4f6d7d752f63784debcee7185d847291d0515c2 Mon Sep 17 00:00:00 2001 From: Alexandr Kitaev Date: Thu, 9 Nov 2023 13:29:12 +0300 Subject: [PATCH 05/10] fix/belt-block version --- belt-kwp/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/belt-kwp/Cargo.toml b/belt-kwp/Cargo.toml index a7dfeef..3ccafcb 100644 --- a/belt-kwp/Cargo.toml +++ b/belt-kwp/Cargo.toml @@ -13,7 +13,7 @@ edition = "2021" rust-version = "1.56" [dependencies] -belt-block = "0.1" +belt-block = "0.1.2" [dev-dependencies] hex-literal = "0.3" From a64b0aeb9c95cdd91a36cf749c108b0ad09ad8f5 Mon Sep 17 00:00:00 2001 From: Alexandr Kitaev Date: Thu, 9 Nov 2023 13:31:11 +0300 Subject: [PATCH 06/10] fix/cargo fmt --- belt-kwp/src/lib.rs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/belt-kwp/src/lib.rs b/belt-kwp/src/lib.rs index 0814947..d7944d2 100644 --- a/belt-kwp/src/lib.rs +++ b/belt-kwp/src/lib.rs @@ -1,8 +1,8 @@ #![no_std] #![doc = include_str!("../README.md")] #![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" + html_logo_url = "https://raw.githubusercontent.com/RustCrypto/media/6ee8e381/logo.svg", + html_favicon_url = "https://raw.githubusercontent.com/RustCrypto/media/6ee8e381/logo.svg" )] #![cfg_attr(docsrs, feature(doc_cfg))] #![forbid(unsafe_code)] @@ -21,8 +21,9 @@ extern crate std; #[cfg(feature = "alloc")] use alloc::vec::Vec; use belt_block::{ + belt_wblock_dec, belt_wblock_enc, cipher::{consts::U32, generic_array::GenericArray}, - to_u32, belt_wblock_dec, belt_wblock_enc, + to_u32, }; /// Block size for BelT-KWP From d42448a43c43d3579c0d0bb8fc832482e2eeda41 Mon Sep 17 00:00:00 2001 From: Alexandr Kitaev Date: Sun, 28 Jul 2024 18:47:58 +0300 Subject: [PATCH 07/10] update kwp to prerelease --- .github/workflows/belt-kwp.yml | 2 +- Cargo.lock | 63 ++++++++++++++++++++++++++++------ Cargo.toml | 3 ++ belt-kwp/Cargo.toml | 6 ++-- belt-kwp/src/lib.rs | 14 ++++---- belt-kwp/src/utils.rs | 14 ++++++++ 6 files changed, 81 insertions(+), 21 deletions(-) create mode 100644 belt-kwp/src/utils.rs diff --git a/.github/workflows/belt-kwp.yml b/.github/workflows/belt-kwp.yml index 8e18fc3..16f9f09 100644 --- a/.github/workflows/belt-kwp.yml +++ b/.github/workflows/belt-kwp.yml @@ -22,7 +22,7 @@ jobs: strategy: matrix: rust: - - 1.56.0 # MSRV + - 1.65.0 # MSRV - stable target: - thumbv7em-none-eabi diff --git a/Cargo.lock b/Cargo.lock index 217d170..f637610 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -10,7 +10,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b169f7a6d4742236a0a00c541b845991d0ac43e546831af1249753ab4c3aa3a0" dependencies = [ "cfg-if", - "cipher", + "cipher 0.4.3", "cpufeatures", ] @@ -19,16 +19,16 @@ name = "aes-kw" version = "0.2.1" dependencies = [ "aes", - "hex-literal", + "hex-literal 0.3.4", ] [[package]] name = "belt-block" -version = "0.1.2" +version = "0.2.0-pre.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9aa1eef3994e2ccd304a78fe3fea4a73e5792007f85f09b79bb82143ca5f82b" +checksum = "3342994f00336827ff9ae0e20e21d38eb88aa4e40aa1795a2a4d14d7af264a5b" dependencies = [ - "cipher", + "cipher 0.5.0-pre.6", ] [[package]] @@ -36,7 +36,7 @@ name = "belt-kwp" version = "0.0.0" dependencies = [ "belt-block", - "hex-literal", + "hex-literal 0.4.1", ] [[package]] @@ -51,8 +51,18 @@ version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d1873270f8f7942c191139cb8a40fd228da6c3fd2fc376d7e92d47aa14aeb59e" dependencies = [ - "crypto-common", - "inout", + "crypto-common 0.1.6", + "inout 0.1.3", +] + +[[package]] +name = "cipher" +version = "0.5.0-pre.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c71c893d5a1e8257048dbb29954d2e1f85f091a150304f1defe4ca2806da5d3f" +dependencies = [ + "crypto-common 0.2.0-rc.0", + "inout 0.2.0-rc.0", ] [[package]] @@ -74,6 +84,15 @@ dependencies = [ "typenum", ] +[[package]] +name = "crypto-common" +version = "0.2.0-rc.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c070b79a496dccd931229780ad5bbedd535ceff6c3565605a8e440e18e1aa2b" +dependencies = [ + "hybrid-array", +] + [[package]] name = "generic-array" version = "0.14.6" @@ -90,6 +109,21 @@ version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7ebdb29d2ea9ed0083cd8cece49bbd968021bd99b0849edb4a9a7ee0fdf6a4e0" +[[package]] +name = "hex-literal" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6fe2267d4ed49bc07b63801559be28c718ea06c4738b7a03c94df7386d2cde46" + +[[package]] +name = "hybrid-array" +version = "0.2.0-rc.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4d306b679262030ad8813a82d4915fc04efff97776e4db7f8eb5137039d56400" +dependencies = [ + "typenum", +] + [[package]] name = "inout" version = "0.1.3" @@ -99,6 +133,15 @@ dependencies = [ "generic-array", ] +[[package]] +name = "inout" +version = "0.2.0-rc.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbc33218cf9ce7b927426ee4ad3501bcc5d8c26bf5fb4a82849a083715aca427" +dependencies = [ + "hybrid-array", +] + [[package]] name = "libc" version = "0.2.134" @@ -107,9 +150,9 @@ checksum = "329c933548736bc49fd575ee68c89e8be4d260064184389a5b77517cddd99ffb" [[package]] name = "typenum" -version = "1.15.0" +version = "1.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dcf81ac59edc17cc8697ff311e8f5ef2d99fcbd9817b34cec66f90b6c3dfd987" +checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" [[package]] name = "version_check" diff --git a/Cargo.toml b/Cargo.toml index 429f12e..d39dbe5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,3 +6,6 @@ members = [ [profile.dev] opt-level = 2 + +#[patch.crates-io] +#belt-block = { git = "https://github.com/RustCrypto/block-ciphers.git" } \ No newline at end of file diff --git a/belt-kwp/Cargo.toml b/belt-kwp/Cargo.toml index 3ccafcb..7cae308 100644 --- a/belt-kwp/Cargo.toml +++ b/belt-kwp/Cargo.toml @@ -10,13 +10,13 @@ keywords = ["crypto", "BELT-KW", "KW", "BELT-KWP", "KWP"] categories = ["cryptography", "no-std"] readme = "README.md" edition = "2021" -rust-version = "1.56" +rust-version = "1.65" [dependencies] -belt-block = "0.1.2" +belt-block = "=0.2.0-pre.1" [dev-dependencies] -hex-literal = "0.3" +hex-literal = "0.4" [features] alloc = [] diff --git a/belt-kwp/src/lib.rs b/belt-kwp/src/lib.rs index d7944d2..2392d1c 100644 --- a/belt-kwp/src/lib.rs +++ b/belt-kwp/src/lib.rs @@ -9,6 +9,7 @@ #![warn(missing_docs, rust_2018_idioms)] mod error; +mod utils; pub use error::{Error, Result}; @@ -22,8 +23,7 @@ extern crate std; use alloc::vec::Vec; use belt_block::{ belt_wblock_dec, belt_wblock_enc, - cipher::{consts::U32, generic_array::GenericArray}, - to_u32, + cipher::{array::Array, consts::U32}, }; /// Block size for BelT-KWP @@ -32,8 +32,8 @@ pub const SEMIBLOCK_LEN: usize = 8; /// Size of an BelT-block "semiblock" in bytes. pub const IV_LEN: usize = 16; -impl From> for BeltKwp { - fn from(kek: GenericArray) -> Self { +impl From> for BeltKwp { + fn from(kek: Array) -> Self { BeltKwp::new(&kek) } } @@ -49,7 +49,7 @@ impl TryFrom<&[u8]> for BeltKwp { fn try_from(value: &[u8]) -> Result { if value.len() == 32 { - Ok(BeltKwp::new(value.into())) + Ok(BeltKwp::new(value.try_into().unwrap())) } else { Err(Error::InvalidKekSize { size: value.len() }) } @@ -66,9 +66,9 @@ pub struct BeltKwp { impl BeltKwp { /// Constructs a new Kek based on the appropriate raw key material. - pub fn new(key: &GenericArray) -> Self { + pub fn new(key: &Array) -> Self { Self { - key: to_u32::<8>(key), + key: utils::to_u32::<8>(key), } } diff --git a/belt-kwp/src/utils.rs b/belt-kwp/src/utils.rs new file mode 100644 index 0000000..e105256 --- /dev/null +++ b/belt-kwp/src/utils.rs @@ -0,0 +1,14 @@ +/// Helper function for transforming BelT keys and blocks from a byte array +/// to an array of `u32`s. +/// +/// # Panics +/// If length of `src` is not equal to `4 * N`. +// #[inline(always)] +pub(crate) fn to_u32(src: &[u8]) -> [u32; N] { + assert_eq!(src.len(), 4 * N); + let mut res = [0u32; N]; + res.iter_mut() + .zip(src.chunks_exact(4)) + .for_each(|(dst, src)| *dst = u32::from_le_bytes(src.try_into().unwrap())); + res +} From 7bfed1d797930d7435b041f4367b80480f78c9dc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=90=D1=80=D1=82=D1=91=D0=BC=20=D0=9F=D0=B0=D0=B2=D0=BB?= =?UTF-8?q?=D0=BE=D0=B2=20=5BArtyom=20Pavlov=5D?= Date: Tue, 5 Nov 2024 19:40:19 +0300 Subject: [PATCH 08/10] bump MSRV --- .github/workflows/belt-kwp.yml | 4 ++-- Cargo.lock | 37 +++++++++++++++++++++++++++------- belt-kwp/Cargo.toml | 4 ++-- 3 files changed, 34 insertions(+), 11 deletions(-) diff --git a/.github/workflows/belt-kwp.yml b/.github/workflows/belt-kwp.yml index 16f9f09..87a690b 100644 --- a/.github/workflows/belt-kwp.yml +++ b/.github/workflows/belt-kwp.yml @@ -22,7 +22,7 @@ jobs: strategy: matrix: rust: - - 1.65.0 # MSRV + - 1.81.0 # MSRV - stable target: - thumbv7em-none-eabi @@ -48,7 +48,7 @@ jobs: strategy: matrix: rust: - - 1.56.0 # MSRV + - 1.81.0 # MSRV - stable steps: - uses: actions/checkout@v4 diff --git a/Cargo.lock b/Cargo.lock index ffb0930..9a2a93a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -20,7 +20,24 @@ version = "0.3.0-pre" dependencies = [ "aes", "const-oid", - "hex-literal", + "hex-literal 0.3.4", +] + +[[package]] +name = "belt-block" +version = "0.2.0-pre.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6df0a14d60362d7b6041d3fe85dfd787ced16c4928f93d72152142c67d01d0bc" +dependencies = [ + "cipher", +] + +[[package]] +name = "belt-kwp" +version = "0.0.0" +dependencies = [ + "belt-block", + "hex-literal 0.4.1", ] [[package]] @@ -47,9 +64,9 @@ checksum = "68ff6be19477a1bd5441f382916a89bc2a0b2c35db6d41e0f6e8538bf6d6463f" [[package]] name = "cpufeatures" -version = "0.2.5" +version = "0.2.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28d997bd5e24a5928dd43e46dc529867e207907fe0b239c3477d924f7f2ca320" +checksum = "608697df725056feaccfa42cffdaeeec3fccc4ffc38358ecd19b243e716a78e0" dependencies = [ "libc", ] @@ -69,6 +86,12 @@ version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7ebdb29d2ea9ed0083cd8cece49bbd968021bd99b0849edb4a9a7ee0fdf6a4e0" +[[package]] +name = "hex-literal" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6fe2267d4ed49bc07b63801559be28c718ea06c4738b7a03c94df7386d2cde46" + [[package]] name = "hybrid-array" version = "0.2.1" @@ -80,18 +103,18 @@ dependencies = [ [[package]] name = "inout" -version = "0.2.0-rc.1" +version = "0.2.0-rc.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "161ac07241f4d11c21b6d82f1fef1c05aec030c0bf568b35281efe453ea450a7" +checksum = "14db49369b2c3f15deb5806de446e05c7f07a2d778b54b278c994fcd1d686f31" dependencies = [ "hybrid-array", ] [[package]] name = "libc" -version = "0.2.134" +version = "0.2.161" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "329c933548736bc49fd575ee68c89e8be4d260064184389a5b77517cddd99ffb" +checksum = "8e9489c2807c139ffd9c1794f4af0ebe86a828db53ecdc7fea2111d0fed085d1" [[package]] name = "typenum" diff --git a/belt-kwp/Cargo.toml b/belt-kwp/Cargo.toml index 7cae308..9c99e0b 100644 --- a/belt-kwp/Cargo.toml +++ b/belt-kwp/Cargo.toml @@ -10,10 +10,10 @@ keywords = ["crypto", "BELT-KW", "KW", "BELT-KWP", "KWP"] categories = ["cryptography", "no-std"] readme = "README.md" edition = "2021" -rust-version = "1.65" +rust-version = "1.81" [dependencies] -belt-block = "=0.2.0-pre.1" +belt-block = "=0.2.0-pre.2" [dev-dependencies] hex-literal = "0.4" From fdfe8a34330ca2091e322006a29af40874faac39 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=90=D1=80=D1=82=D1=91=D0=BC=20=D0=9F=D0=B0=D0=B2=D0=BB?= =?UTF-8?q?=D0=BE=D0=B2=20=5BArtyom=20Pavlov=5D?= Date: Tue, 5 Nov 2024 20:43:39 +0300 Subject: [PATCH 09/10] tweak code --- Cargo.toml | 1 + belt-kwp/Cargo.toml | 5 +- belt-kwp/LICENSE-MIT | 2 +- belt-kwp/README.md | 59 ++++++------ belt-kwp/src/error.rs | 49 ---------- belt-kwp/src/lib.rs | 183 ++++++++++++++++++------------------ belt-kwp/src/utils.rs | 14 --- belt-kwp/tests/kwp_tests.rs | 43 --------- belt-kwp/tests/mod.rs | 39 ++++++++ 9 files changed, 164 insertions(+), 231 deletions(-) delete mode 100644 belt-kwp/src/error.rs delete mode 100644 belt-kwp/src/utils.rs delete mode 100644 belt-kwp/tests/kwp_tests.rs create mode 100644 belt-kwp/tests/mod.rs diff --git a/Cargo.toml b/Cargo.toml index d39dbe5..456f416 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,4 +1,5 @@ [workspace] +resolver = "2" members = [ "aes-kw", "belt-kwp", diff --git a/belt-kwp/Cargo.toml b/belt-kwp/Cargo.toml index 9c99e0b..a93a03b 100644 --- a/belt-kwp/Cargo.toml +++ b/belt-kwp/Cargo.toml @@ -4,8 +4,7 @@ version = "0.0.0" description = "STB 34.101.30-2020 Key Wrap Algorithm (KWP) implementation using Belt block cipher" authors = ["RustCrypto Developers"] license = "MIT OR Apache-2.0" -homepage = "https://github.com/RustCrypto/key-wraps/" -repository = "https://github.com/RustCrypto/key-wraps/tree/belt-kwp" +repository = "https://github.com/RustCrypto/key-wraps" keywords = ["crypto", "BELT-KW", "KW", "BELT-KWP", "KWP"] categories = ["cryptography", "no-std"] readme = "README.md" @@ -24,4 +23,4 @@ std = ["alloc"] [package.metadata.docs.rs] all-features = true -rustdoc-args = ["--cfg", "docsrs"] \ No newline at end of file +rustdoc-args = ["--cfg", "docsrs"] diff --git a/belt-kwp/LICENSE-MIT b/belt-kwp/LICENSE-MIT index a2b36f2..e7068ac 100644 --- a/belt-kwp/LICENSE-MIT +++ b/belt-kwp/LICENSE-MIT @@ -1,4 +1,4 @@ -Copyright (c) 2021-2022 RustCrypto Developers +Copyright (c) 2024 RustCrypto Developers Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated diff --git a/belt-kwp/README.md b/belt-kwp/README.md index a7a052d..b942772 100644 --- a/belt-kwp/README.md +++ b/belt-kwp/README.md @@ -6,42 +6,44 @@ ![Rust Version][rustc-image] [![Build Status][build-image]][build-link] -Pure Rust implementation of the [BelT Key Wrap]. +Pure Rust implementation of the `belt-kwp` key wrapping algorithm defined in [STB 4.101.31-2020]. -# Usage +[STB 4.101.31-2020]: https://apmi.bsu.by/assets/files/std/belt-spec371.pdf -The most common way to use BelT-KWP is as follows: you provide the Key Wrapping Key and the key-to-be-wrapped, then wrap it, or provide a wrapped-key and unwrap it. +# Examples ```rust -# fn main() -> Result<(), Box> { -# #[cfg(feature = "std")] -# { use hex_literal::hex; -use belt_kwp::BeltKwp; -let x = hex!("B194BAC8 0A08F53B 366D008E 584A5DE4 8504FA9D 1BB6C7AC 252E72C2 02FDCE0D"); -let i = hex!("5BE3D612 17B96181 FE6786AD 716B890B"); -let k = hex!("E9DEE72C 8F0C0FA6 2DDB49F4 6F739647 06075316 ED247A37 39CBA383 03A98BF6"); -let y = hex!("49A38EE1 08D6C742 E52B774F 00A6EF98 B106CBD1 3EA4FB06 80323051 BC04DF76 E487B055 C69BCF54 1176169F 1DC9F6C8"); - -let mut wrapped = [0u8; 48]; -let mut unwrapped = [0u8; 48]; - -let kek = BeltKwp::new(&k.into()); - -kek.wrap_key(&x, &i, &mut wrapped).unwrap(); -assert_eq!(y, wrapped); - -kek.unwrap_key(&y, &i, &mut unwrapped).unwrap(); -assert_eq!(x, unwrapped[..32]); -# } -# Ok(()) -# } +let x: [u8; 32] = hex!( + "B194BAC8 0A08F53B 366D008E 584A5DE4" + "8504FA9D 1BB6C7AC 252E72C2 02FDCE0D" +); +let i: [u8; 16] = hex!("5BE3D612 17B96181 FE6786AD 716B890B"); +let k: [u8; 32] = hex!( + "E9DEE72C 8F0C0FA6 2DDB49F4 6F739647" + "06075316 ED247A37 39CBA383 03A98BF6" +); +let y: [u8; 48] = hex!( + "49A38EE1 08D6C742 E52B774F 00A6EF98" + "B106CBD1 3EA4FB06 80323051 BC04DF76" + "E487B055 C69BCF54 1176169F 1DC9F6C8" +); + +let mut buf = [0u8; 48]; + +let kw = belt_kwp::BeltKwp::new(&k); + +let wrapped_key = kw.wrap_key(&x, &i, &mut buf).unwrap(); +assert_eq!(y, wrapped_key); + +let unwrapped_key = kw.unwrap_key(&y, &i, &mut buf).unwrap(); +assert_eq!(x, unwrapped_key); ``` ## Minimum Supported Rust Version -This crate requires **Rust 1.56** at a minimum. +This crate requires **Rust 1.81** at a minimum. We may change the MSRV in the future, but it will be accompanied by a minor version bump. @@ -68,9 +70,6 @@ dual licensed as above, without any additional terms or conditions. [docs-image]: https://docs.rs/belt-kwp/badge.svg [docs-link]: https://docs.rs/belt-kwp/ [license-image]: https://img.shields.io/badge/license-Apache2.0/MIT-blue.svg -[rustc-image]: https://img.shields.io/badge/rustc-1.56+-blue.svg +[rustc-image]: https://img.shields.io/badge/rustc-1.81+-blue.svg [build-image]: https://github.com/RustCrypto/key-wraps/actions/workflows/belt-kwp.yml/badge.svg [build-link]: https://github.com/RustCrypto/key-wraps/actions/workflows/belt-kwp.yml - -[//]: # (links) -[BelT Key Wrap]: https://apmi.bsu.by/assets/files/std/belt-spec371.pdf diff --git a/belt-kwp/src/error.rs b/belt-kwp/src/error.rs deleted file mode 100644 index 4f1b993..0000000 --- a/belt-kwp/src/error.rs +++ /dev/null @@ -1,49 +0,0 @@ -use core::fmt; - -/// Result type with the `belt-kwp` crate's [`Error`]. -pub type Result = core::result::Result; - -/// Errors emitted from the wrap and unwrap operations. -#[derive(Debug)] -pub enum Error { - /// Input data length invalid. - InvalidDataSize, - - /// Invalid KEK size. - InvalidKekSize { - /// KEK size provided in bytes (expected 32). - size: usize, - }, - - /// Output buffer size invalid. - InvalidOutputSize { - /// Expected size in bytes. - expected: usize, - }, - - /// Integrity check did not pass. - IntegrityCheckFailed, -} - -impl fmt::Display for Error { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self { - Error::InvalidDataSize => write!( - f, - "data must be a multiple of 64 bits and more than 16 bytes" - ), - Error::InvalidOutputSize { expected } => { - write!(f, "invalid output buffer size: expected {}", expected) - } - Error::IntegrityCheckFailed => { - write!(f, "integrity check failed") - } - Error::InvalidKekSize { size } => { - write!(f, "invalid KEK size: {}", size) - } - } - } -} - -#[cfg(feature = "std")] -impl std::error::Error for Error {} diff --git a/belt-kwp/src/lib.rs b/belt-kwp/src/lib.rs index 2392d1c..5383af1 100644 --- a/belt-kwp/src/lib.rs +++ b/belt-kwp/src/lib.rs @@ -4,136 +4,137 @@ html_logo_url = "https://raw.githubusercontent.com/RustCrypto/media/6ee8e381/logo.svg", html_favicon_url = "https://raw.githubusercontent.com/RustCrypto/media/6ee8e381/logo.svg" )] -#![cfg_attr(docsrs, feature(doc_cfg))] +#![cfg_attr(docsrs, feature(doc_auto_cfg))] #![forbid(unsafe_code)] #![warn(missing_docs, rust_2018_idioms)] -mod error; -mod utils; +use belt_block::{belt_wblock_dec, belt_wblock_enc}; +use core::fmt; -pub use error::{Error, Result}; - -#[cfg(feature = "alloc")] -#[macro_use] -extern crate alloc; -#[cfg(feature = "std")] -extern crate std; - -#[cfg(feature = "alloc")] -use alloc::vec::Vec; -use belt_block::{ - belt_wblock_dec, belt_wblock_enc, - cipher::{array::Array, consts::U32}, -}; - -/// Block size for BelT-KWP -pub const SEMIBLOCK_LEN: usize = 8; - -/// Size of an BelT-block "semiblock" in bytes. +/// Size of wrapping "header". pub const IV_LEN: usize = 16; -impl From> for BeltKwp { - fn from(kek: Array) -> Self { - BeltKwp::new(&kek) - } -} - -impl From<[u8; 32]> for BeltKwp { - fn from(kek: [u8; 32]) -> Self { - BeltKwp::new(&kek.into()) - } +/// BelT Key Wrap instancce as defined in STB 34.101.34-2020. +#[derive(Clone, Copy, PartialEq)] +pub struct BeltKwp { + key: [u32; 8], } -impl TryFrom<&[u8]> for BeltKwp { - type Error = Error; - - fn try_from(value: &[u8]) -> Result { - if value.len() == 32 { - Ok(BeltKwp::new(value.try_into().unwrap())) - } else { - Err(Error::InvalidKekSize { size: value.len() }) - } +impl fmt::Debug for BeltKwp { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str("BeltKwp { ... }") } } -/// A Key-Encrypting-Key (KEK) that can be used to wrap and unwrap other -/// keys. -#[derive(Debug, Clone, Copy, PartialEq)] -pub struct BeltKwp { - /// Initialized key - key: [u32; 8], -} - impl BeltKwp { - /// Constructs a new Kek based on the appropriate raw key material. - pub fn new(key: &Array) -> Self { - Self { - key: utils::to_u32::<8>(key), - } + /// Create new [`BeltKwp`] instance. + #[inline] + pub fn new(key: &[u8; 32]) -> Self { + let mut res = [0u32; 8]; + res.iter_mut() + .zip(key.chunks_exact(4)) + .for_each(|(dst, src)| *dst = u32::from_le_bytes(src.try_into().unwrap())); + Self { key: res } } - /// BelT Key Wrap, as defined in STB 34.101.34-2020. + /// Wrap key `x` with given `iv` and write result to `out`. /// - /// The `out` buffer will be overwritten, and must be exactly [`IV_LEN`] - /// bytes (i.e. 16 bytes) longer than the length of `data`. - pub fn wrap_key(&self, x: &[u8], iv: &[u8], out: &mut [u8]) -> Result<()> { - if x.len() % SEMIBLOCK_LEN != 0 || x.len() < 32 || iv.len() != IV_LEN { + /// Size of `x` must be bigger than 16 bytes. + /// Size of `out` must be bigger or equal to x.len() + [IV_LEN]. + #[inline] + pub fn wrap_key<'a>( + &self, + x: &[u8], + iv: &[u8; IV_LEN], + out: &'a mut [u8], + ) -> Result<&'a [u8], Error> { + if x.len() < 16 { return Err(Error::InvalidDataSize); } - if out.len() != (x.len() + IV_LEN) { + let out_len = x.len() + IV_LEN; + if out.len() < out_len { return Err(Error::InvalidOutputSize { expected: x.len() + IV_LEN, }); } + let out = &mut out[..out_len]; - out[..x.len()].copy_from_slice(x); - out[x.len()..].copy_from_slice(iv); + let (l, r) = out.split_at_mut(x.len()); + l.copy_from_slice(x); + r.copy_from_slice(iv); - // 1. Y ← belt-wblock(X || I, K) - belt_wblock_enc(out, &self.key).map_err(|_| Error::InvalidDataSize) + belt_wblock_enc(out, &self.key).map_err(|_| Error::InvalidDataSize)?; + Ok(out) } - /// BelT Key Unwrap, as defined in STB 34.101.31-2020. + /// Unwrap key in `y` with given `iv` and write result to `out`. /// - /// The `out` buffer will be overwritten, and it length must be exactly the length of `data`. - pub fn unwrap_key(&self, y: &[u8], iv: &[u8], out: &mut [u8]) -> Result<()> { - // 1. If |Y| mod 8 ≠ 0 or |Y| < 32 or |I| ≠ 16 then return error - if y.len() % SEMIBLOCK_LEN != 0 || y.len() < 32 || iv.len() != IV_LEN { + /// Size of wrapped data `y` must be bigger or equal to 32 bytes. + /// Size of `out` must be bigger or equal to the size of `y`. + #[inline] + pub fn unwrap_key<'a>( + &self, + y: &[u8], + iv: &[u8; IV_LEN], + out: &'a mut [u8], + ) -> Result<&'a [u8], Error> { + if y.len() < 32 { return Err(Error::InvalidDataSize); } - if out.len() != y.len() { + if out.len() < y.len() { return Err(Error::InvalidOutputSize { expected: y.len() }); } + let out = &mut out[..y.len()]; out.copy_from_slice(y); - // 2. (X || r) ← belt-wblock^(-1)(Y, K) belt_wblock_dec(out, &self.key).map_err(|_| Error::InvalidDataSize)?; - // 3. If r ≠ I then return error - if &out[y.len() - IV_LEN..] != iv { - return Err(Error::IntegrityCheckFailed); - } - Ok(()) - } + let (key, rem) = out.split_at_mut(y.len() - IV_LEN); - /// Computes [`Self::wrap`], allocating a [`Vec`] for the return value. - #[cfg(feature = "alloc")] - #[cfg_attr(docsrs, doc(cfg(feature = "alloc")))] - pub fn wrap_vec(&self, data: &[u8], iv: &[u8]) -> Result> { - let mut out = vec![0u8; data.len() + IV_LEN]; - self.wrap_key(data, iv, &mut out)?; - Ok(out) + let calc_iv = u128::from_ne_bytes(rem.try_into().unwrap()); + let expected_iv = u128::from_ne_bytes(*iv); + // We expect that comparison of `u128`s will be constant-time + if calc_iv == expected_iv { + Ok(key) + } else { + key.fill(0); + rem.fill(0); + Err(Error::IntegrityCheckFailed) + } } +} - /// Computes [`Self::unwrap`], allocating a [`Vec`] for the return value. - #[cfg(feature = "alloc")] - #[cfg_attr(docsrs, doc(cfg(feature = "alloc")))] - pub fn unwrap_vec(&self, data: &[u8], iv: &[u8]) -> Result> { - let mut out = vec![0u8; data.len()]; - self.unwrap_key(data, iv, &mut out)?; - Ok(out) +/// Errors emitted from the wrap and unwrap operations. +#[derive(Debug)] +pub enum Error { + /// Input data length invalid. + InvalidDataSize, + + /// Output buffer size invalid. + InvalidOutputSize { + /// Expected size in bytes. + expected: usize, + }, + + /// Integrity check did not pass. + IntegrityCheckFailed, +} + +impl fmt::Display for Error { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Error::InvalidDataSize => write!(f, "invalid data size"), + Error::InvalidOutputSize { expected } => { + write!(f, "invalid output buffer size: expected {expected}") + } + Error::IntegrityCheckFailed => { + write!(f, "integrity check failed") + } + } } } + +impl core::error::Error for Error {} diff --git a/belt-kwp/src/utils.rs b/belt-kwp/src/utils.rs deleted file mode 100644 index e105256..0000000 --- a/belt-kwp/src/utils.rs +++ /dev/null @@ -1,14 +0,0 @@ -/// Helper function for transforming BelT keys and blocks from a byte array -/// to an array of `u32`s. -/// -/// # Panics -/// If length of `src` is not equal to `4 * N`. -// #[inline(always)] -pub(crate) fn to_u32(src: &[u8]) -> [u32; N] { - assert_eq!(src.len(), 4 * N); - let mut res = [0u32; N]; - res.iter_mut() - .zip(src.chunks_exact(4)) - .for_each(|(dst, src)| *dst = u32::from_le_bytes(src.try_into().unwrap())); - res -} diff --git a/belt-kwp/tests/kwp_tests.rs b/belt-kwp/tests/kwp_tests.rs deleted file mode 100644 index b5bb3c6..0000000 --- a/belt-kwp/tests/kwp_tests.rs +++ /dev/null @@ -1,43 +0,0 @@ -#[cfg(test)] -mod tests { - use belt_kwp::BeltKwp; - use hex_literal::hex; - - #[test] - fn test_key_wrap() { - let x = hex!("B194BAC8 0A08F53B 366D008E 584A5DE4 8504FA9D 1BB6C7AC 252E72C2 02FDCE0D"); - let i = hex!("5BE3D612 17B96181 FE6786AD 716B890B"); - let k = hex!("E9DEE72C 8F0C0FA6 2DDB49F4 6F739647 06075316 ED247A37 39CBA383 03A98BF6"); - let y = hex!("49A38EE1 08D6C742 E52B774F 00A6EF98 B106CBD1 3EA4FB06 80323051 BC04DF76 E487B055 C69BCF54 1176169F 1DC9F6C8"); - - let mut wrapped = [0u8; 48]; - let mut unwrapped = [0u8; 48]; - - let kek = BeltKwp::new(&k.into()); - - kek.wrap_key(&x, &i, &mut wrapped).unwrap(); - assert_eq!(y, wrapped); - - kek.unwrap_key(&y, &i, &mut unwrapped).unwrap(); - assert_eq!(x, unwrapped[..32]); - } - - #[test] - fn test_key_unwrap() { - let y = hex!("49A38EE1 08D6C742 E52B774F 00A6EF98 B106CBD1 3EA4FB06 80323051 BC04DF76 E487B055 C69BCF54 1176169F 1DC9F6C8"); - let i = hex!("5BE3D612 17B96181 FE6786AD 716B890B"); - let k = hex!("E9DEE72C 8F0C0FA6 2DDB49F4 6F739647 06075316 ED247A37 39CBA383 03A98BF6"); - let x = hex!("B194BAC8 0A08F53B 366D008E 584A5DE4 8504FA9D 1BB6C7AC 252E72C2 02FDCE0D"); - - let mut unwrapped = [0u8; 48]; - let mut wrapped = [0u8; 48]; - - let kek = BeltKwp::new(&k.into()); - - kek.unwrap_key(&y, &i, &mut unwrapped).unwrap(); - assert_eq!(x, unwrapped[..32]); - - kek.wrap_key(&x, &i, &mut wrapped).unwrap(); - assert_eq!(y, wrapped); - } -} diff --git a/belt-kwp/tests/mod.rs b/belt-kwp/tests/mod.rs new file mode 100644 index 0000000..3415f6f --- /dev/null +++ b/belt-kwp/tests/mod.rs @@ -0,0 +1,39 @@ +//! Test vectors from STB 4.101.31-2020 (section A.10, tables A.21-A.22): +//! https://apmi.bsu.by/assets/files/std/belt-spec371.pdf +use belt_kwp::BeltKwp; +use hex_literal::hex; + +#[test] +fn belt_kwp() { + // Table A.21 + let x1 = hex!("B194BAC8 0A08F53B 366D008E 584A5DE4 8504FA9D 1BB6C7AC 252E72C2 02FDCE0D"); + let i1 = hex!("5BE3D612 17B96181 FE6786AD 716B890B"); + let k1 = hex!("E9DEE72C 8F0C0FA6 2DDB49F4 6F739647 06075316 ED247A37 39CBA383 03A98BF6"); + let y1 = hex!( + "49A38EE1 08D6C742 E52B774F 00A6EF98 B106CBD1 3EA4FB06 80323051 BC04DF76" + "E487B055 C69BCF54 1176169F 1DC9F6C8" + ); + + // Table A.22 + let x2 = hex!("92632EE0 C21AD9E0 9A39343E 5C07DAA4 889B03F2 E6847EB1 52EC99F7 A4D9F154"); + let i2 = hex!("B5EF68D8 E4A39E56 7153DE13 D72254EE"); + let k2 = hex!("92BD9B1C E5D14101 5445FBC9 5E4D0EF2 682080AA 227D642F 2687F934 90405511"); + let y2 = hex!( + "E12BDC1A E28257EC 703FCCF0 95EE8DF1 C1AB7638 9FE678CA F7C6F860 D5BB9C4F" + "F33C657B 637C306A DD4EA779 9EB23D31" + ); + + let mut buf = [0u8; 48]; + + let kw = BeltKwp::new(&k1); + let res = kw.wrap_key(&x1, &i1, &mut buf).unwrap(); + assert_eq!(y1, res); + let res = kw.unwrap_key(&y1, &i1, &mut buf).unwrap(); + assert_eq!(x1, res); + + let kw = BeltKwp::new(&k2); + let res = kw.wrap_key(&x2, &i2, &mut buf).unwrap(); + assert_eq!(y2, res); + let res = kw.unwrap_key(&y2, &i2, &mut buf).unwrap(); + assert_eq!(x2, res); +} From 42f12e7464cfaf93ba69b8a91d6db0e099f6521f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=90=D1=80=D1=82=D1=91=D0=BC=20=D0=9F=D0=B0=D0=B2=D0=BB?= =?UTF-8?q?=D0=BE=D0=B2=20=5BArtyom=20Pavlov=5D?= Date: Tue, 5 Nov 2024 20:46:40 +0300 Subject: [PATCH 10/10] remove commented lines --- Cargo.toml | 3 --- 1 file changed, 3 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 456f416..2ba4f51 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -7,6 +7,3 @@ members = [ [profile.dev] opt-level = 2 - -#[patch.crates-io] -#belt-block = { git = "https://github.com/RustCrypto/block-ciphers.git" } \ No newline at end of file