diff --git a/.github/workflows/belt-kwp.yml b/.github/workflows/belt-kwp.yml new file mode 100644 index 0000000..87a690b --- /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.81.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.81.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 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/Cargo.toml b/Cargo.toml index a25ce31..2ba4f51 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,8 @@ [workspace] +resolver = "2" members = [ "aes-kw", + "belt-kwp", ] [profile.dev] diff --git a/belt-kwp/Cargo.toml b/belt-kwp/Cargo.toml new file mode 100644 index 0000000..a93a03b --- /dev/null +++ b/belt-kwp/Cargo.toml @@ -0,0 +1,26 @@ +[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" +repository = "https://github.com/RustCrypto/key-wraps" +keywords = ["crypto", "BELT-KW", "KW", "BELT-KWP", "KWP"] +categories = ["cryptography", "no-std"] +readme = "README.md" +edition = "2021" +rust-version = "1.81" + +[dependencies] +belt-block = "=0.2.0-pre.2" + +[dev-dependencies] +hex-literal = "0.4" + +[features] +alloc = [] +std = ["alloc"] + +[package.metadata.docs.rs] +all-features = true +rustdoc-args = ["--cfg", "docsrs"] 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..e7068ac --- /dev/null +++ b/belt-kwp/LICENSE-MIT @@ -0,0 +1,25 @@ +Copyright (c) 2024 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..b942772 --- /dev/null +++ b/belt-kwp/README.md @@ -0,0 +1,75 @@ +# 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-kwp` key wrapping algorithm defined in [STB 4.101.31-2020]. + +[STB 4.101.31-2020]: https://apmi.bsu.by/assets/files/std/belt-spec371.pdf + +# Examples + +```rust +use hex_literal::hex; + +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.81** 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.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 diff --git a/belt-kwp/src/lib.rs b/belt-kwp/src/lib.rs new file mode 100644 index 0000000..5383af1 --- /dev/null +++ b/belt-kwp/src/lib.rs @@ -0,0 +1,140 @@ +#![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_auto_cfg))] +#![forbid(unsafe_code)] +#![warn(missing_docs, rust_2018_idioms)] + +use belt_block::{belt_wblock_dec, belt_wblock_enc}; +use core::fmt; + +/// Size of wrapping "header". +pub const IV_LEN: usize = 16; + +/// BelT Key Wrap instancce as defined in STB 34.101.34-2020. +#[derive(Clone, Copy, PartialEq)] +pub struct BeltKwp { + key: [u32; 8], +} + +impl fmt::Debug for BeltKwp { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str("BeltKwp { ... }") + } +} + +impl BeltKwp { + /// 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 } + } + + /// Wrap key `x` with given `iv` and write result to `out`. + /// + /// 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); + } + + 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]; + + let (l, r) = out.split_at_mut(x.len()); + l.copy_from_slice(x); + r.copy_from_slice(iv); + + belt_wblock_enc(out, &self.key).map_err(|_| Error::InvalidDataSize)?; + Ok(out) + } + + /// Unwrap key in `y` with given `iv` and write result to `out`. + /// + /// 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() { + return Err(Error::InvalidOutputSize { expected: y.len() }); + } + + let out = &mut out[..y.len()]; + out.copy_from_slice(y); + + belt_wblock_dec(out, &self.key).map_err(|_| Error::InvalidDataSize)?; + + let (key, rem) = out.split_at_mut(y.len() - IV_LEN); + + 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) + } + } +} + +/// 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/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); +}