kuznyechik-0.8.2/.cargo_vcs_info.json0000644000000001500000000000100132230ustar { "git": { "sha1": "a8daf37101e9747691fe61356c8993809688fa2c" }, "path_in_vcs": "kuznyechik" }kuznyechik-0.8.2/CHANGELOG.md000064400000000000000000000041601046102023000136310ustar 00000000000000# 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.8.2 (2023-08-06) ### Fixed - `Drop` implementations in the software backend with enabled `zeroize` feature ([#311]) [#311]: https://github.com/RustCrypto/block-ciphers/pull/311 ## 0.8.1 (2022-02-17) ### Fixed - Minimal versions build ([#303]) [#303]: https://github.com/RustCrypto/block-ciphers/pull/303 ## 0.8.0 (2022-02-10) ### Changed - Bump `cipher` dependency to v0.4 ([#284]) ### Added - Encrypt-only `KuznyechikEnc` and decrypt-only `KuznyechikDec` types ([#284]) [#284]: https://github.com/RustCrypto/block-ciphers/pull/284 ## 0.7.2 (2021-08-26) ### Added - Parallel block processing for SSE2 backend ([#278]) [#278]: https://github.com/RustCrypto/block-ciphers/pull/278 ## 0.7.1 (2021-05-20) ### Added - SSE2-based implementation ([#261]) [#261]: https://github.com/RustCrypto/block-ciphers/pull/261 ## 0.7.0 (2021-04-29) ### Changed - Bump `cipher` dependency to v0.3 ([#235]) [#235]: https://github.com/RustCrypto/block-ciphers/pull/235 ## 0.6.0 (2020-10-16) ### Changed - Replace `block-cipher`/`stream-cipher` with `cipher` crate ([#167]) [#167]: https://github.com/RustCrypto/block-ciphers/pull/167 ## 0.5.0 (2020-08-07) ### Changed - Bump `block-cipher` dependency to v0.8 ([#138]) - Bump `opaque-debug` dependency to v0.3 ([#140]) [#138]: https://github.com/RustCrypto/block-ciphers/pull/138 [#140]: https://github.com/RustCrypto/block-ciphers/pull/140 ## 0.4.1 (2020-07-10) ### Changed - Improved performance by unrolling loops ([#137]) [#137]: https://github.com/RustCrypto/block-ciphers/pull/137 ## 0.4.0 (2020-07-03) ### Changed - Bump `block-cipher` dependency to v0.7 ([#92]) - Upgrade to Rust 2018 edition ([#92]) ### Fixed - Byte order ([#118]) [#118]: https://github.com/RustCrypto/block-ciphers/pull/118 [#92]: https://github.com/RustCrypto/block-ciphers/pull/92 ## 0.3.0 (2018-12-23) ## 0.2.0 (2017-11-26) ## 0.1.1 (2017-01-12) ## 0.1.0 (2017-01-12) kuznyechik-0.8.2/Cargo.toml0000644000000023300000000000100112230ustar # THIS FILE IS AUTOMATICALLY GENERATED BY CARGO # # When uploading crates to the registry Cargo will automatically # "normalize" Cargo.toml files for maximal compatibility # with all versions of Cargo and also rewrite `path` dependencies # to registry (e.g., crates.io) dependencies. # # If you are reading this file be aware that the original Cargo.toml # will likely look very different (and much more reasonable). # See Cargo.toml.orig for the original contents. [package] edition = "2021" rust-version = "1.56" name = "kuznyechik" version = "0.8.2" authors = ["RustCrypto Developers"] description = "Kuznyechik (GOST R 34.12-2015) block cipher" documentation = "https://docs.rs/kuznyechik" readme = "README.md" keywords = [ "crypto", "kuznyechik", "gost", "block-cipher", ] categories = [ "cryptography", "no-std", ] license = "MIT OR Apache-2.0" repository = "https://github.com/RustCrypto/block-ciphers" resolver = "1" [package.metadata.docs.rs] all-features = true rustdoc-args = [ "--cfg", "docsrs", ] [dependencies.cipher] version = "0.4.2" [dev-dependencies.cipher] version = "0.4.2" features = ["dev"] [dev-dependencies.hex-literal] version = "0.3.3" [features] zeroize = ["cipher/zeroize"] kuznyechik-0.8.2/Cargo.toml.orig0000644000000012600000000000100121630ustar [package] name = "kuznyechik" version = "0.8.2" description = "Kuznyechik (GOST R 34.12-2015) block cipher" authors = ["RustCrypto Developers"] license = "MIT OR Apache-2.0" edition = "2021" rust-version = "1.56" readme = "README.md" documentation = "https://docs.rs/kuznyechik" repository = "https://github.com/RustCrypto/block-ciphers" keywords = ["crypto", "kuznyechik", "gost", "block-cipher"] categories = ["cryptography", "no-std"] [dependencies] cipher = "0.4.2" [dev-dependencies] cipher = { version = "0.4.2", features = ["dev"] } hex-literal = "0.3.3" [features] zeroize = ["cipher/zeroize"] [package.metadata.docs.rs] all-features = true rustdoc-args = ["--cfg", "docsrs"] kuznyechik-0.8.2/Cargo.toml.orig000064400000000000000000000012601046102023000147050ustar 00000000000000[package] name = "kuznyechik" version = "0.8.2" description = "Kuznyechik (GOST R 34.12-2015) block cipher" authors = ["RustCrypto Developers"] license = "MIT OR Apache-2.0" edition = "2021" rust-version = "1.56" readme = "README.md" documentation = "https://docs.rs/kuznyechik" repository = "https://github.com/RustCrypto/block-ciphers" keywords = ["crypto", "kuznyechik", "gost", "block-cipher"] categories = ["cryptography", "no-std"] [dependencies] cipher = "0.4.2" [dev-dependencies] cipher = { version = "0.4.2", features = ["dev"] } hex-literal = "0.3.3" [features] zeroize = ["cipher/zeroize"] [package.metadata.docs.rs] all-features = true rustdoc-args = ["--cfg", "docsrs"] kuznyechik-0.8.2/LICENSE-APACHE000064400000000000000000000251411046102023000137460ustar 00000000000000 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. kuznyechik-0.8.2/LICENSE-MIT000064400000000000000000000020411046102023000134500ustar 00000000000000Copyright (c) 2017 Artyom Pavlov 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. kuznyechik-0.8.2/README.md000064400000000000000000000046001046102023000132760ustar 00000000000000# RustCrypto: Kuznyechik Cipher [![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] [![HAZMAT][hazmat-image]][hazmat-link] Pure Rust implementation of the Kuznyechik (GOST R 34.12-2015) block cipher [Documentation][docs-link] ## ⚠️ Security Warning: [Hazmat!][hazmat-link] This crate does not ensure ciphertexts are authentic (i.e. by using a MAC to verify ciphertext integrity), which can lead to serious vulnerabilities if used incorrectly! No security audits of this crate have ever been performed, and it has not been thoroughly assessed to ensure its operation is constant-time on common CPU architectures. USE AT YOUR OWN RISK! ## Minimum Supported Rust Version Rust **1.56** 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/kuznyechik.svg [crate-link]: https://crates.io/crates/kuznyechik [docs-image]: https://docs.rs/kuznyechik/badge.svg [docs-link]: https://docs.rs/kuznyechik/ [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 [hazmat-image]: https://img.shields.io/badge/crypto-hazmat%E2%9A%A0-red.svg [hazmat-link]: https://github.com/RustCrypto/meta/blob/master/HAZMAT.md [chat-image]: https://img.shields.io/badge/zulip-join_chat-blue.svg [chat-link]: https://rustcrypto.zulipchat.com/#narrow/stream/260039-block-ciphers [build-image]: https://github.com/RustCrypto/block-ciphers/workflows/kuznyechik/badge.svg?branch=master&event=push [build-link]: https://github.com/RustCrypto/block-ciphers/actions?query=workflow%3Akuznyechik kuznyechik-0.8.2/src/consts.rs000064400000000000000000000065511046102023000144740ustar 00000000000000/// Substitution table pub const P: [u8; 256] = [ 0xFC, 0xEE, 0xDD, 0x11, 0xCF, 0x6E, 0x31, 0x16, 0xFB, 0xC4, 0xFA, 0xDA, 0x23, 0xC5, 0x04, 0x4D, 0xE9, 0x77, 0xF0, 0xDB, 0x93, 0x2E, 0x99, 0xBA, 0x17, 0x36, 0xF1, 0xBB, 0x14, 0xCD, 0x5F, 0xC1, 0xF9, 0x18, 0x65, 0x5A, 0xE2, 0x5C, 0xEF, 0x21, 0x81, 0x1C, 0x3C, 0x42, 0x8B, 0x01, 0x8E, 0x4F, 0x05, 0x84, 0x02, 0xAE, 0xE3, 0x6A, 0x8F, 0xA0, 0x06, 0x0B, 0xED, 0x98, 0x7F, 0xD4, 0xD3, 0x1F, 0xEB, 0x34, 0x2C, 0x51, 0xEA, 0xC8, 0x48, 0xAB, 0xF2, 0x2A, 0x68, 0xA2, 0xFD, 0x3A, 0xCE, 0xCC, 0xB5, 0x70, 0x0E, 0x56, 0x08, 0x0C, 0x76, 0x12, 0xBF, 0x72, 0x13, 0x47, 0x9C, 0xB7, 0x5D, 0x87, 0x15, 0xA1, 0x96, 0x29, 0x10, 0x7B, 0x9A, 0xC7, 0xF3, 0x91, 0x78, 0x6F, 0x9D, 0x9E, 0xB2, 0xB1, 0x32, 0x75, 0x19, 0x3D, 0xFF, 0x35, 0x8A, 0x7E, 0x6D, 0x54, 0xC6, 0x80, 0xC3, 0xBD, 0x0D, 0x57, 0xDF, 0xF5, 0x24, 0xA9, 0x3E, 0xA8, 0x43, 0xC9, 0xD7, 0x79, 0xD6, 0xF6, 0x7C, 0x22, 0xB9, 0x03, 0xE0, 0x0F, 0xEC, 0xDE, 0x7A, 0x94, 0xB0, 0xBC, 0xDC, 0xE8, 0x28, 0x50, 0x4E, 0x33, 0x0A, 0x4A, 0xA7, 0x97, 0x60, 0x73, 0x1E, 0x00, 0x62, 0x44, 0x1A, 0xB8, 0x38, 0x82, 0x64, 0x9F, 0x26, 0x41, 0xAD, 0x45, 0x46, 0x92, 0x27, 0x5E, 0x55, 0x2F, 0x8C, 0xA3, 0xA5, 0x7D, 0x69, 0xD5, 0x95, 0x3B, 0x07, 0x58, 0xB3, 0x40, 0x86, 0xAC, 0x1D, 0xF7, 0x30, 0x37, 0x6B, 0xE4, 0x88, 0xD9, 0xE7, 0x89, 0xE1, 0x1B, 0x83, 0x49, 0x4C, 0x3F, 0xF8, 0xFE, 0x8D, 0x53, 0xAA, 0x90, 0xCA, 0xD8, 0x85, 0x61, 0x20, 0x71, 0x67, 0xA4, 0x2D, 0x2B, 0x09, 0x5B, 0xCB, 0x9B, 0x25, 0xD0, 0xBE, 0xE5, 0x6C, 0x52, 0x59, 0xA6, 0x74, 0xD2, 0xE6, 0xF4, 0xB4, 0xC0, 0xD1, 0x66, 0xAF, 0xC2, 0x39, 0x4B, 0x63, 0xB6, ]; /// Inverse ubstitution table pub const P_INV: [u8; 256] = [ 0xA5, 0x2D, 0x32, 0x8F, 0x0E, 0x30, 0x38, 0xC0, 0x54, 0xE6, 0x9E, 0x39, 0x55, 0x7E, 0x52, 0x91, 0x64, 0x03, 0x57, 0x5A, 0x1C, 0x60, 0x07, 0x18, 0x21, 0x72, 0xA8, 0xD1, 0x29, 0xC6, 0xA4, 0x3F, 0xE0, 0x27, 0x8D, 0x0C, 0x82, 0xEA, 0xAE, 0xB4, 0x9A, 0x63, 0x49, 0xE5, 0x42, 0xE4, 0x15, 0xB7, 0xC8, 0x06, 0x70, 0x9D, 0x41, 0x75, 0x19, 0xC9, 0xAA, 0xFC, 0x4D, 0xBF, 0x2A, 0x73, 0x84, 0xD5, 0xC3, 0xAF, 0x2B, 0x86, 0xA7, 0xB1, 0xB2, 0x5B, 0x46, 0xD3, 0x9F, 0xFD, 0xD4, 0x0F, 0x9C, 0x2F, 0x9B, 0x43, 0xEF, 0xD9, 0x79, 0xB6, 0x53, 0x7F, 0xC1, 0xF0, 0x23, 0xE7, 0x25, 0x5E, 0xB5, 0x1E, 0xA2, 0xDF, 0xA6, 0xFE, 0xAC, 0x22, 0xF9, 0xE2, 0x4A, 0xBC, 0x35, 0xCA, 0xEE, 0x78, 0x05, 0x6B, 0x51, 0xE1, 0x59, 0xA3, 0xF2, 0x71, 0x56, 0x11, 0x6A, 0x89, 0x94, 0x65, 0x8C, 0xBB, 0x77, 0x3C, 0x7B, 0x28, 0xAB, 0xD2, 0x31, 0xDE, 0xC4, 0x5F, 0xCC, 0xCF, 0x76, 0x2C, 0xB8, 0xD8, 0x2E, 0x36, 0xDB, 0x69, 0xB3, 0x14, 0x95, 0xBE, 0x62, 0xA1, 0x3B, 0x16, 0x66, 0xE9, 0x5C, 0x6C, 0x6D, 0xAD, 0x37, 0x61, 0x4B, 0xB9, 0xE3, 0xBA, 0xF1, 0xA0, 0x85, 0x83, 0xDA, 0x47, 0xC5, 0xB0, 0x33, 0xFA, 0x96, 0x6F, 0x6E, 0xC2, 0xF6, 0x50, 0xFF, 0x5D, 0xA9, 0x8E, 0x17, 0x1B, 0x97, 0x7D, 0xEC, 0x58, 0xF7, 0x1F, 0xFB, 0x7C, 0x09, 0x0D, 0x7A, 0x67, 0x45, 0x87, 0xDC, 0xE8, 0x4F, 0x1D, 0x4E, 0x04, 0xEB, 0xF8, 0xF3, 0x3E, 0x3D, 0xBD, 0x8A, 0x88, 0xDD, 0xCD, 0x0B, 0x13, 0x98, 0x02, 0x93, 0x80, 0x90, 0xD0, 0x24, 0x34, 0xCB, 0xED, 0xF4, 0xCE, 0x99, 0x10, 0x44, 0x40, 0x92, 0x3A, 0x01, 0x26, 0x12, 0x1A, 0x48, 0x68, 0xF5, 0x81, 0x8B, 0xC7, 0xD6, 0x20, 0x0A, 0x08, 0x00, 0x4C, 0xD7, 0x74, ]; #[test] fn test_subst_tables() { for i in 0..256 { assert_eq!(P_INV[P[i] as usize], i as u8); } } kuznyechik-0.8.2/src/lib.rs000064400000000000000000000035001046102023000137200ustar 00000000000000//! Pure Rust implementation of the [Kuznyechik] ([GOST R 34.12-2015]) block cipher. //! //! # ⚠️ Security Warning: Hazmat! //! //! This crate implements only the low-level block cipher function, and is intended //! for use for implementing higher-level constructions *only*. It is NOT //! intended for direct use in applications. //! //! USE AT YOUR OWN RISK! //! //! # Configuration Flags //! //! You can modify crate using the following configuration flag: //! //! - `kuznyechik_force_soft`: force software implementation. //! //! It can be enabled using `RUSTFLAGS` environmental variable //! (e.g. `RUSTFLAGS="--cfg kuznyechik_force_soft"`) or by modifying //! `.cargo/config`. //! //! [Kuznyechik]: https://en.wikipedia.org/wiki/Kuznyechik //! [GOST R 34.12-2015]: https://tc26.ru/standard/gost/GOST_R_3412-2015.pdf #![no_std] #![doc( html_logo_url = "https://raw.githubusercontent.com/RustCrypto/media/26acc39f/logo.svg", html_favicon_url = "https://raw.githubusercontent.com/RustCrypto/media/26acc39f/logo.svg" )] #![cfg_attr(docsrs, feature(doc_cfg))] #![warn(missing_docs, rust_2018_idioms)] #![allow(clippy::needless_range_loop, clippy::transmute_ptr_to_ptr)] pub use cipher; use cipher::{ consts::{U16, U32}, generic_array::GenericArray, }; mod consts; #[cfg(all( any(target_arch = "x86_64", target_arch = "x86"), target_feature = "sse2", not(kuznyechik_force_soft), ))] #[path = "sse2/mod.rs"] mod imp; #[cfg(not(all( any(target_arch = "x86_64", target_arch = "x86"), target_feature = "sse2", not(kuznyechik_force_soft), )))] #[path = "soft/mod.rs"] mod imp; pub use imp::{Kuznyechik, KuznyechikDec, KuznyechikEnc}; type BlockSize = U16; type KeySize = U32; /// 128-bit Kuznyechik block pub type Block = GenericArray; /// 256-bit Kuznyechik key pub type Key = GenericArray; kuznyechik-0.8.2/src/soft/backends.rs000064400000000000000000000073671046102023000157160ustar 00000000000000use super::consts::GF; use crate::consts::{P, P_INV}; use crate::{Block, Key}; use cipher::{ consts::{U1, U16}, inout::InOut, BlockBackend, BlockSizeUser, ParBlocks, ParBlocksSizeUser, }; pub(super) type RoundKeys = [Block; 10]; #[inline(always)] fn x(a: &mut Block, b: &Block) { for i in 0..16 { a[i] ^= b[i]; } } fn l_step(msg: &mut Block, i: usize) { #[inline(always)] fn get_idx(b: usize, i: usize) -> usize { b.wrapping_sub(i) & 0x0F } #[inline(always)] fn get_m(msg: &Block, b: usize, i: usize) -> usize { msg[get_idx(b, i)] as usize } let mut x = msg[get_idx(15, i)]; x ^= GF[3][get_m(msg, 14, i)]; x ^= GF[1][get_m(msg, 13, i)]; x ^= GF[2][get_m(msg, 12, i)]; x ^= GF[0][get_m(msg, 11, i)]; x ^= GF[5][get_m(msg, 10, i)]; x ^= GF[4][get_m(msg, 9, i)]; x ^= msg[get_idx(8, i)]; x ^= GF[6][get_m(msg, 7, i)]; x ^= msg[get_idx(6, i)]; x ^= GF[4][get_m(msg, 5, i)]; x ^= GF[5][get_m(msg, 4, i)]; x ^= GF[0][get_m(msg, 3, i)]; x ^= GF[2][get_m(msg, 2, i)]; x ^= GF[1][get_m(msg, 1, i)]; x ^= GF[3][get_m(msg, 0, i)]; msg[get_idx(15, i)] = x; } #[inline(always)] fn lsx(block: &mut Block, key: &Block) { x(block, key); // s for i in 0..16 { block[i] = P[block[i] as usize]; } // l for i in 0..16 { l_step(block, i); } } #[inline(always)] fn lsx_inv(block: &mut Block, key: &Block) { x(block, key); // l_inv for i in 0..16 { l_step(block, 15 - i); } // s_inv for i in 0..16 { block[15 - i] = P_INV[block[15 - i] as usize]; } } fn get_c(n: usize) -> Block { let mut v = Block::default(); v[15] = n as u8; for i in 0..16 { l_step(&mut v, i); } v } fn f(k1: &mut Block, k2: &mut Block, n: usize) { for i in 0..4 { let mut k1_cpy = *k1; lsx(&mut k1_cpy, &get_c(8 * n + 2 * i + 1)); x(k2, &k1_cpy); let mut k2_cpy = *k2; lsx(&mut k2_cpy, &get_c(8 * n + 2 * i + 2)); x(k1, &k2_cpy); } } pub(super) fn expand(key: &Key) -> RoundKeys { let mut keys = RoundKeys::default(); let mut k1 = Block::default(); let mut k2 = Block::default(); k1.copy_from_slice(&key[..16]); k2.copy_from_slice(&key[16..]); keys[0] = k1; keys[1] = k2; for i in 1..5 { f(&mut k1, &mut k2, i - 1); keys[2 * i] = k1; keys[2 * i + 1] = k2; } keys } pub(crate) struct EncBackend<'a>(pub(crate) &'a RoundKeys); impl<'a> BlockSizeUser for EncBackend<'a> { type BlockSize = U16; } impl<'a> ParBlocksSizeUser for EncBackend<'a> { type ParBlocksSize = U1; } impl<'a> BlockBackend for EncBackend<'a> { #[inline] fn proc_block(&mut self, mut block: InOut<'_, '_, Block>) { let mut b = *block.get_in(); for i in 0..9 { lsx(&mut b, &self.0[i]); } x(&mut b, &self.0[9]); *block.get_out() = b; } #[inline(always)] fn proc_par_blocks(&mut self, mut blocks: InOut<'_, '_, ParBlocks>) { self.proc_block(blocks.get(0)); } } pub(crate) struct DecBackend<'a>(pub(crate) &'a RoundKeys); impl<'a> BlockSizeUser for DecBackend<'a> { type BlockSize = U16; } impl<'a> ParBlocksSizeUser for DecBackend<'a> { type ParBlocksSize = U1; } impl<'a> BlockBackend for DecBackend<'a> { #[inline] fn proc_block(&mut self, mut block: InOut<'_, '_, Block>) { let mut b = *block.get_in(); for i in 0..9 { lsx_inv(&mut b, &self.0[9 - i]); } x(&mut b, &self.0[0]); *block.get_out() = b; } #[inline(always)] fn proc_par_blocks(&mut self, mut blocks: InOut<'_, '_, ParBlocks>) { self.proc_block(blocks.get(0)); } } kuznyechik-0.8.2/src/soft/consts.rs000064400000000000000000000272451046102023000154520ustar 00000000000000// Precomputed values for Galois field multiplication pub const GF: [[u8; 256]; 7] = [ [ 0x00, 0x10, 0x20, 0x30, 0x40, 0x50, 0x60, 0x70, 0x80, 0x90, 0xA0, 0xB0, 0xC0, 0xD0, 0xE0, 0xF0, 0xC3, 0xD3, 0xE3, 0xF3, 0x83, 0x93, 0xA3, 0xB3, 0x43, 0x53, 0x63, 0x73, 0x03, 0x13, 0x23, 0x33, 0x45, 0x55, 0x65, 0x75, 0x05, 0x15, 0x25, 0x35, 0xC5, 0xD5, 0xE5, 0xF5, 0x85, 0x95, 0xA5, 0xB5, 0x86, 0x96, 0xA6, 0xB6, 0xC6, 0xD6, 0xE6, 0xF6, 0x06, 0x16, 0x26, 0x36, 0x46, 0x56, 0x66, 0x76, 0x8A, 0x9A, 0xAA, 0xBA, 0xCA, 0xDA, 0xEA, 0xFA, 0x0A, 0x1A, 0x2A, 0x3A, 0x4A, 0x5A, 0x6A, 0x7A, 0x49, 0x59, 0x69, 0x79, 0x09, 0x19, 0x29, 0x39, 0xC9, 0xD9, 0xE9, 0xF9, 0x89, 0x99, 0xA9, 0xB9, 0xCF, 0xDF, 0xEF, 0xFF, 0x8F, 0x9F, 0xAF, 0xBF, 0x4F, 0x5F, 0x6F, 0x7F, 0x0F, 0x1F, 0x2F, 0x3F, 0x0C, 0x1C, 0x2C, 0x3C, 0x4C, 0x5C, 0x6C, 0x7C, 0x8C, 0x9C, 0xAC, 0xBC, 0xCC, 0xDC, 0xEC, 0xFC, 0xD7, 0xC7, 0xF7, 0xE7, 0x97, 0x87, 0xB7, 0xA7, 0x57, 0x47, 0x77, 0x67, 0x17, 0x07, 0x37, 0x27, 0x14, 0x04, 0x34, 0x24, 0x54, 0x44, 0x74, 0x64, 0x94, 0x84, 0xB4, 0xA4, 0xD4, 0xC4, 0xF4, 0xE4, 0x92, 0x82, 0xB2, 0xA2, 0xD2, 0xC2, 0xF2, 0xE2, 0x12, 0x02, 0x32, 0x22, 0x52, 0x42, 0x72, 0x62, 0x51, 0x41, 0x71, 0x61, 0x11, 0x01, 0x31, 0x21, 0xD1, 0xC1, 0xF1, 0xE1, 0x91, 0x81, 0xB1, 0xA1, 0x5D, 0x4D, 0x7D, 0x6D, 0x1D, 0x0D, 0x3D, 0x2D, 0xDD, 0xCD, 0xFD, 0xED, 0x9D, 0x8D, 0xBD, 0xAD, 0x9E, 0x8E, 0xBE, 0xAE, 0xDE, 0xCE, 0xFE, 0xEE, 0x1E, 0x0E, 0x3E, 0x2E, 0x5E, 0x4E, 0x7E, 0x6E, 0x18, 0x08, 0x38, 0x28, 0x58, 0x48, 0x78, 0x68, 0x98, 0x88, 0xB8, 0xA8, 0xD8, 0xC8, 0xF8, 0xE8, 0xDB, 0xCB, 0xFB, 0xEB, 0x9B, 0x8B, 0xBB, 0xAB, 0x5B, 0x4B, 0x7B, 0x6B, 0x1B, 0x0B, 0x3B, 0x2B, ], [ 0x00, 0x20, 0x40, 0x60, 0x80, 0xA0, 0xC0, 0xE0, 0xC3, 0xE3, 0x83, 0xA3, 0x43, 0x63, 0x03, 0x23, 0x45, 0x65, 0x05, 0x25, 0xC5, 0xE5, 0x85, 0xA5, 0x86, 0xA6, 0xC6, 0xE6, 0x06, 0x26, 0x46, 0x66, 0x8A, 0xAA, 0xCA, 0xEA, 0x0A, 0x2A, 0x4A, 0x6A, 0x49, 0x69, 0x09, 0x29, 0xC9, 0xE9, 0x89, 0xA9, 0xCF, 0xEF, 0x8F, 0xAF, 0x4F, 0x6F, 0x0F, 0x2F, 0x0C, 0x2C, 0x4C, 0x6C, 0x8C, 0xAC, 0xCC, 0xEC, 0xD7, 0xF7, 0x97, 0xB7, 0x57, 0x77, 0x17, 0x37, 0x14, 0x34, 0x54, 0x74, 0x94, 0xB4, 0xD4, 0xF4, 0x92, 0xB2, 0xD2, 0xF2, 0x12, 0x32, 0x52, 0x72, 0x51, 0x71, 0x11, 0x31, 0xD1, 0xF1, 0x91, 0xB1, 0x5D, 0x7D, 0x1D, 0x3D, 0xDD, 0xFD, 0x9D, 0xBD, 0x9E, 0xBE, 0xDE, 0xFE, 0x1E, 0x3E, 0x5E, 0x7E, 0x18, 0x38, 0x58, 0x78, 0x98, 0xB8, 0xD8, 0xF8, 0xDB, 0xFB, 0x9B, 0xBB, 0x5B, 0x7B, 0x1B, 0x3B, 0x6D, 0x4D, 0x2D, 0x0D, 0xED, 0xCD, 0xAD, 0x8D, 0xAE, 0x8E, 0xEE, 0xCE, 0x2E, 0x0E, 0x6E, 0x4E, 0x28, 0x08, 0x68, 0x48, 0xA8, 0x88, 0xE8, 0xC8, 0xEB, 0xCB, 0xAB, 0x8B, 0x6B, 0x4B, 0x2B, 0x0B, 0xE7, 0xC7, 0xA7, 0x87, 0x67, 0x47, 0x27, 0x07, 0x24, 0x04, 0x64, 0x44, 0xA4, 0x84, 0xE4, 0xC4, 0xA2, 0x82, 0xE2, 0xC2, 0x22, 0x02, 0x62, 0x42, 0x61, 0x41, 0x21, 0x01, 0xE1, 0xC1, 0xA1, 0x81, 0xBA, 0x9A, 0xFA, 0xDA, 0x3A, 0x1A, 0x7A, 0x5A, 0x79, 0x59, 0x39, 0x19, 0xF9, 0xD9, 0xB9, 0x99, 0xFF, 0xDF, 0xBF, 0x9F, 0x7F, 0x5F, 0x3F, 0x1F, 0x3C, 0x1C, 0x7C, 0x5C, 0xBC, 0x9C, 0xFC, 0xDC, 0x30, 0x10, 0x70, 0x50, 0xB0, 0x90, 0xF0, 0xD0, 0xF3, 0xD3, 0xB3, 0x93, 0x73, 0x53, 0x33, 0x13, 0x75, 0x55, 0x35, 0x15, 0xF5, 0xD5, 0xB5, 0x95, 0xB6, 0x96, 0xF6, 0xD6, 0x36, 0x16, 0x76, 0x56, ], [ 0x00, 0x85, 0xC9, 0x4C, 0x51, 0xD4, 0x98, 0x1D, 0xA2, 0x27, 0x6B, 0xEE, 0xF3, 0x76, 0x3A, 0xBF, 0x87, 0x02, 0x4E, 0xCB, 0xD6, 0x53, 0x1F, 0x9A, 0x25, 0xA0, 0xEC, 0x69, 0x74, 0xF1, 0xBD, 0x38, 0xCD, 0x48, 0x04, 0x81, 0x9C, 0x19, 0x55, 0xD0, 0x6F, 0xEA, 0xA6, 0x23, 0x3E, 0xBB, 0xF7, 0x72, 0x4A, 0xCF, 0x83, 0x06, 0x1B, 0x9E, 0xD2, 0x57, 0xE8, 0x6D, 0x21, 0xA4, 0xB9, 0x3C, 0x70, 0xF5, 0x59, 0xDC, 0x90, 0x15, 0x08, 0x8D, 0xC1, 0x44, 0xFB, 0x7E, 0x32, 0xB7, 0xAA, 0x2F, 0x63, 0xE6, 0xDE, 0x5B, 0x17, 0x92, 0x8F, 0x0A, 0x46, 0xC3, 0x7C, 0xF9, 0xB5, 0x30, 0x2D, 0xA8, 0xE4, 0x61, 0x94, 0x11, 0x5D, 0xD8, 0xC5, 0x40, 0x0C, 0x89, 0x36, 0xB3, 0xFF, 0x7A, 0x67, 0xE2, 0xAE, 0x2B, 0x13, 0x96, 0xDA, 0x5F, 0x42, 0xC7, 0x8B, 0x0E, 0xB1, 0x34, 0x78, 0xFD, 0xE0, 0x65, 0x29, 0xAC, 0xB2, 0x37, 0x7B, 0xFE, 0xE3, 0x66, 0x2A, 0xAF, 0x10, 0x95, 0xD9, 0x5C, 0x41, 0xC4, 0x88, 0x0D, 0x35, 0xB0, 0xFC, 0x79, 0x64, 0xE1, 0xAD, 0x28, 0x97, 0x12, 0x5E, 0xDB, 0xC6, 0x43, 0x0F, 0x8A, 0x7F, 0xFA, 0xB6, 0x33, 0x2E, 0xAB, 0xE7, 0x62, 0xDD, 0x58, 0x14, 0x91, 0x8C, 0x09, 0x45, 0xC0, 0xF8, 0x7D, 0x31, 0xB4, 0xA9, 0x2C, 0x60, 0xE5, 0x5A, 0xDF, 0x93, 0x16, 0x0B, 0x8E, 0xC2, 0x47, 0xEB, 0x6E, 0x22, 0xA7, 0xBA, 0x3F, 0x73, 0xF6, 0x49, 0xCC, 0x80, 0x05, 0x18, 0x9D, 0xD1, 0x54, 0x6C, 0xE9, 0xA5, 0x20, 0x3D, 0xB8, 0xF4, 0x71, 0xCE, 0x4B, 0x07, 0x82, 0x9F, 0x1A, 0x56, 0xD3, 0x26, 0xA3, 0xEF, 0x6A, 0x77, 0xF2, 0xBE, 0x3B, 0x84, 0x01, 0x4D, 0xC8, 0xD5, 0x50, 0x1C, 0x99, 0xA1, 0x24, 0x68, 0xED, 0xF0, 0x75, 0x39, 0xBC, 0x03, 0x86, 0xCA, 0x4F, 0x52, 0xD7, 0x9B, 0x1E, ], [ 0x00, 0x94, 0xEB, 0x7F, 0x15, 0x81, 0xFE, 0x6A, 0x2A, 0xBE, 0xC1, 0x55, 0x3F, 0xAB, 0xD4, 0x40, 0x54, 0xC0, 0xBF, 0x2B, 0x41, 0xD5, 0xAA, 0x3E, 0x7E, 0xEA, 0x95, 0x01, 0x6B, 0xFF, 0x80, 0x14, 0xA8, 0x3C, 0x43, 0xD7, 0xBD, 0x29, 0x56, 0xC2, 0x82, 0x16, 0x69, 0xFD, 0x97, 0x03, 0x7C, 0xE8, 0xFC, 0x68, 0x17, 0x83, 0xE9, 0x7D, 0x02, 0x96, 0xD6, 0x42, 0x3D, 0xA9, 0xC3, 0x57, 0x28, 0xBC, 0x93, 0x07, 0x78, 0xEC, 0x86, 0x12, 0x6D, 0xF9, 0xB9, 0x2D, 0x52, 0xC6, 0xAC, 0x38, 0x47, 0xD3, 0xC7, 0x53, 0x2C, 0xB8, 0xD2, 0x46, 0x39, 0xAD, 0xED, 0x79, 0x06, 0x92, 0xF8, 0x6C, 0x13, 0x87, 0x3B, 0xAF, 0xD0, 0x44, 0x2E, 0xBA, 0xC5, 0x51, 0x11, 0x85, 0xFA, 0x6E, 0x04, 0x90, 0xEF, 0x7B, 0x6F, 0xFB, 0x84, 0x10, 0x7A, 0xEE, 0x91, 0x05, 0x45, 0xD1, 0xAE, 0x3A, 0x50, 0xC4, 0xBB, 0x2F, 0xE5, 0x71, 0x0E, 0x9A, 0xF0, 0x64, 0x1B, 0x8F, 0xCF, 0x5B, 0x24, 0xB0, 0xDA, 0x4E, 0x31, 0xA5, 0xB1, 0x25, 0x5A, 0xCE, 0xA4, 0x30, 0x4F, 0xDB, 0x9B, 0x0F, 0x70, 0xE4, 0x8E, 0x1A, 0x65, 0xF1, 0x4D, 0xD9, 0xA6, 0x32, 0x58, 0xCC, 0xB3, 0x27, 0x67, 0xF3, 0x8C, 0x18, 0x72, 0xE6, 0x99, 0x0D, 0x19, 0x8D, 0xF2, 0x66, 0x0C, 0x98, 0xE7, 0x73, 0x33, 0xA7, 0xD8, 0x4C, 0x26, 0xB2, 0xCD, 0x59, 0x76, 0xE2, 0x9D, 0x09, 0x63, 0xF7, 0x88, 0x1C, 0x5C, 0xC8, 0xB7, 0x23, 0x49, 0xDD, 0xA2, 0x36, 0x22, 0xB6, 0xC9, 0x5D, 0x37, 0xA3, 0xDC, 0x48, 0x08, 0x9C, 0xE3, 0x77, 0x1D, 0x89, 0xF6, 0x62, 0xDE, 0x4A, 0x35, 0xA1, 0xCB, 0x5F, 0x20, 0xB4, 0xF4, 0x60, 0x1F, 0x8B, 0xE1, 0x75, 0x0A, 0x9E, 0x8A, 0x1E, 0x61, 0xF5, 0x9F, 0x0B, 0x74, 0xE0, 0xA0, 0x34, 0x4B, 0xDF, 0xB5, 0x21, 0x5E, 0xCA, ], [ 0x00, 0xC0, 0x43, 0x83, 0x86, 0x46, 0xC5, 0x05, 0xCF, 0x0F, 0x8C, 0x4C, 0x49, 0x89, 0x0A, 0xCA, 0x5D, 0x9D, 0x1E, 0xDE, 0xDB, 0x1B, 0x98, 0x58, 0x92, 0x52, 0xD1, 0x11, 0x14, 0xD4, 0x57, 0x97, 0xBA, 0x7A, 0xF9, 0x39, 0x3C, 0xFC, 0x7F, 0xBF, 0x75, 0xB5, 0x36, 0xF6, 0xF3, 0x33, 0xB0, 0x70, 0xE7, 0x27, 0xA4, 0x64, 0x61, 0xA1, 0x22, 0xE2, 0x28, 0xE8, 0x6B, 0xAB, 0xAE, 0x6E, 0xED, 0x2D, 0xB7, 0x77, 0xF4, 0x34, 0x31, 0xF1, 0x72, 0xB2, 0x78, 0xB8, 0x3B, 0xFB, 0xFE, 0x3E, 0xBD, 0x7D, 0xEA, 0x2A, 0xA9, 0x69, 0x6C, 0xAC, 0x2F, 0xEF, 0x25, 0xE5, 0x66, 0xA6, 0xA3, 0x63, 0xE0, 0x20, 0x0D, 0xCD, 0x4E, 0x8E, 0x8B, 0x4B, 0xC8, 0x08, 0xC2, 0x02, 0x81, 0x41, 0x44, 0x84, 0x07, 0xC7, 0x50, 0x90, 0x13, 0xD3, 0xD6, 0x16, 0x95, 0x55, 0x9F, 0x5F, 0xDC, 0x1C, 0x19, 0xD9, 0x5A, 0x9A, 0xAD, 0x6D, 0xEE, 0x2E, 0x2B, 0xEB, 0x68, 0xA8, 0x62, 0xA2, 0x21, 0xE1, 0xE4, 0x24, 0xA7, 0x67, 0xF0, 0x30, 0xB3, 0x73, 0x76, 0xB6, 0x35, 0xF5, 0x3F, 0xFF, 0x7C, 0xBC, 0xB9, 0x79, 0xFA, 0x3A, 0x17, 0xD7, 0x54, 0x94, 0x91, 0x51, 0xD2, 0x12, 0xD8, 0x18, 0x9B, 0x5B, 0x5E, 0x9E, 0x1D, 0xDD, 0x4A, 0x8A, 0x09, 0xC9, 0xCC, 0x0C, 0x8F, 0x4F, 0x85, 0x45, 0xC6, 0x06, 0x03, 0xC3, 0x40, 0x80, 0x1A, 0xDA, 0x59, 0x99, 0x9C, 0x5C, 0xDF, 0x1F, 0xD5, 0x15, 0x96, 0x56, 0x53, 0x93, 0x10, 0xD0, 0x47, 0x87, 0x04, 0xC4, 0xC1, 0x01, 0x82, 0x42, 0x88, 0x48, 0xCB, 0x0B, 0x0E, 0xCE, 0x4D, 0x8D, 0xA0, 0x60, 0xE3, 0x23, 0x26, 0xE6, 0x65, 0xA5, 0x6F, 0xAF, 0x2C, 0xEC, 0xE9, 0x29, 0xAA, 0x6A, 0xFD, 0x3D, 0xBE, 0x7E, 0x7B, 0xBB, 0x38, 0xF8, 0x32, 0xF2, 0x71, 0xB1, 0xB4, 0x74, 0xF7, 0x37, ], [ 0x00, 0xC2, 0x47, 0x85, 0x8E, 0x4C, 0xC9, 0x0B, 0xDF, 0x1D, 0x98, 0x5A, 0x51, 0x93, 0x16, 0xD4, 0x7D, 0xBF, 0x3A, 0xF8, 0xF3, 0x31, 0xB4, 0x76, 0xA2, 0x60, 0xE5, 0x27, 0x2C, 0xEE, 0x6B, 0xA9, 0xFA, 0x38, 0xBD, 0x7F, 0x74, 0xB6, 0x33, 0xF1, 0x25, 0xE7, 0x62, 0xA0, 0xAB, 0x69, 0xEC, 0x2E, 0x87, 0x45, 0xC0, 0x02, 0x09, 0xCB, 0x4E, 0x8C, 0x58, 0x9A, 0x1F, 0xDD, 0xD6, 0x14, 0x91, 0x53, 0x37, 0xF5, 0x70, 0xB2, 0xB9, 0x7B, 0xFE, 0x3C, 0xE8, 0x2A, 0xAF, 0x6D, 0x66, 0xA4, 0x21, 0xE3, 0x4A, 0x88, 0x0D, 0xCF, 0xC4, 0x06, 0x83, 0x41, 0x95, 0x57, 0xD2, 0x10, 0x1B, 0xD9, 0x5C, 0x9E, 0xCD, 0x0F, 0x8A, 0x48, 0x43, 0x81, 0x04, 0xC6, 0x12, 0xD0, 0x55, 0x97, 0x9C, 0x5E, 0xDB, 0x19, 0xB0, 0x72, 0xF7, 0x35, 0x3E, 0xFC, 0x79, 0xBB, 0x6F, 0xAD, 0x28, 0xEA, 0xE1, 0x23, 0xA6, 0x64, 0x6E, 0xAC, 0x29, 0xEB, 0xE0, 0x22, 0xA7, 0x65, 0xB1, 0x73, 0xF6, 0x34, 0x3F, 0xFD, 0x78, 0xBA, 0x13, 0xD1, 0x54, 0x96, 0x9D, 0x5F, 0xDA, 0x18, 0xCC, 0x0E, 0x8B, 0x49, 0x42, 0x80, 0x05, 0xC7, 0x94, 0x56, 0xD3, 0x11, 0x1A, 0xD8, 0x5D, 0x9F, 0x4B, 0x89, 0x0C, 0xCE, 0xC5, 0x07, 0x82, 0x40, 0xE9, 0x2B, 0xAE, 0x6C, 0x67, 0xA5, 0x20, 0xE2, 0x36, 0xF4, 0x71, 0xB3, 0xB8, 0x7A, 0xFF, 0x3D, 0x59, 0x9B, 0x1E, 0xDC, 0xD7, 0x15, 0x90, 0x52, 0x86, 0x44, 0xC1, 0x03, 0x08, 0xCA, 0x4F, 0x8D, 0x24, 0xE6, 0x63, 0xA1, 0xAA, 0x68, 0xED, 0x2F, 0xFB, 0x39, 0xBC, 0x7E, 0x75, 0xB7, 0x32, 0xF0, 0xA3, 0x61, 0xE4, 0x26, 0x2D, 0xEF, 0x6A, 0xA8, 0x7C, 0xBE, 0x3B, 0xF9, 0xF2, 0x30, 0xB5, 0x77, 0xDE, 0x1C, 0x99, 0x5B, 0x50, 0x92, 0x17, 0xD5, 0x01, 0xC3, 0x46, 0x84, 0x8F, 0x4D, 0xC8, 0x0A, ], [ 0x00, 0xFB, 0x35, 0xCE, 0x6A, 0x91, 0x5F, 0xA4, 0xD4, 0x2F, 0xE1, 0x1A, 0xBE, 0x45, 0x8B, 0x70, 0x6B, 0x90, 0x5E, 0xA5, 0x01, 0xFA, 0x34, 0xCF, 0xBF, 0x44, 0x8A, 0x71, 0xD5, 0x2E, 0xE0, 0x1B, 0xD6, 0x2D, 0xE3, 0x18, 0xBC, 0x47, 0x89, 0x72, 0x02, 0xF9, 0x37, 0xCC, 0x68, 0x93, 0x5D, 0xA6, 0xBD, 0x46, 0x88, 0x73, 0xD7, 0x2C, 0xE2, 0x19, 0x69, 0x92, 0x5C, 0xA7, 0x03, 0xF8, 0x36, 0xCD, 0x6F, 0x94, 0x5A, 0xA1, 0x05, 0xFE, 0x30, 0xCB, 0xBB, 0x40, 0x8E, 0x75, 0xD1, 0x2A, 0xE4, 0x1F, 0x04, 0xFF, 0x31, 0xCA, 0x6E, 0x95, 0x5B, 0xA0, 0xD0, 0x2B, 0xE5, 0x1E, 0xBA, 0x41, 0x8F, 0x74, 0xB9, 0x42, 0x8C, 0x77, 0xD3, 0x28, 0xE6, 0x1D, 0x6D, 0x96, 0x58, 0xA3, 0x07, 0xFC, 0x32, 0xC9, 0xD2, 0x29, 0xE7, 0x1C, 0xB8, 0x43, 0x8D, 0x76, 0x06, 0xFD, 0x33, 0xC8, 0x6C, 0x97, 0x59, 0xA2, 0xDE, 0x25, 0xEB, 0x10, 0xB4, 0x4F, 0x81, 0x7A, 0x0A, 0xF1, 0x3F, 0xC4, 0x60, 0x9B, 0x55, 0xAE, 0xB5, 0x4E, 0x80, 0x7B, 0xDF, 0x24, 0xEA, 0x11, 0x61, 0x9A, 0x54, 0xAF, 0x0B, 0xF0, 0x3E, 0xC5, 0x08, 0xF3, 0x3D, 0xC6, 0x62, 0x99, 0x57, 0xAC, 0xDC, 0x27, 0xE9, 0x12, 0xB6, 0x4D, 0x83, 0x78, 0x63, 0x98, 0x56, 0xAD, 0x09, 0xF2, 0x3C, 0xC7, 0xB7, 0x4C, 0x82, 0x79, 0xDD, 0x26, 0xE8, 0x13, 0xB1, 0x4A, 0x84, 0x7F, 0xDB, 0x20, 0xEE, 0x15, 0x65, 0x9E, 0x50, 0xAB, 0x0F, 0xF4, 0x3A, 0xC1, 0xDA, 0x21, 0xEF, 0x14, 0xB0, 0x4B, 0x85, 0x7E, 0x0E, 0xF5, 0x3B, 0xC0, 0x64, 0x9F, 0x51, 0xAA, 0x67, 0x9C, 0x52, 0xA9, 0x0D, 0xF6, 0x38, 0xC3, 0xB3, 0x48, 0x86, 0x7D, 0xD9, 0x22, 0xEC, 0x17, 0x0C, 0xF7, 0x39, 0xC2, 0x66, 0x9D, 0x53, 0xA8, 0xD8, 0x23, 0xED, 0x16, 0xB2, 0x49, 0x87, 0x7C, ], ]; kuznyechik-0.8.2/src/soft/mod.rs000064400000000000000000000114471046102023000147150ustar 00000000000000use crate::{BlockSize, Key, KeySize}; use cipher::{ AlgorithmName, BlockCipher, BlockClosure, BlockDecrypt, BlockEncrypt, BlockSizeUser, KeyInit, KeySizeUser, }; use core::fmt; #[cfg(feature = "zeroize")] use cipher::zeroize::{Zeroize, ZeroizeOnDrop}; mod backends; mod consts; use backends::{DecBackend, EncBackend, RoundKeys}; /// Kuznyechik (GOST R 34.12-2015) block cipher #[derive(Clone)] pub struct Kuznyechik { keys: RoundKeys, } impl BlockCipher for Kuznyechik {} impl KeySizeUser for Kuznyechik { type KeySize = KeySize; } impl BlockSizeUser for Kuznyechik { type BlockSize = BlockSize; } impl KeyInit for Kuznyechik { fn new(key: &Key) -> Self { Self { keys: backends::expand(key), } } } impl From for Kuznyechik { #[inline] fn from(enc: KuznyechikEnc) -> Kuznyechik { Self { keys: enc.keys } } } impl From<&KuznyechikEnc> for Kuznyechik { #[inline] fn from(enc: &KuznyechikEnc) -> Kuznyechik { Self { keys: enc.keys.clone(), } } } impl BlockEncrypt for Kuznyechik { fn encrypt_with_backend(&self, f: impl BlockClosure) { f.call(&mut EncBackend(&self.keys)); } } impl BlockDecrypt for Kuznyechik { fn decrypt_with_backend(&self, f: impl BlockClosure) { f.call(&mut DecBackend(&self.keys)); } } impl fmt::Debug for Kuznyechik { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { f.write_str("Kuznyechik { ... }") } } impl AlgorithmName for Kuznyechik { fn write_alg_name(f: &mut fmt::Formatter<'_>) -> fmt::Result { f.write_str("Kuznyechik") } } #[cfg(feature = "zeroize")] #[cfg_attr(docsrs, doc(cfg(feature = "zeroize")))] impl Drop for Kuznyechik { fn drop(&mut self) { self.keys.iter_mut().for_each(|key| key.zeroize()); } } #[cfg(feature = "zeroize")] #[cfg_attr(docsrs, doc(cfg(feature = "zeroize")))] impl ZeroizeOnDrop for Kuznyechik {} /// Kuznyechik (GOST R 34.12-2015) block cipher (encrypt-only) #[derive(Clone)] pub struct KuznyechikEnc { keys: RoundKeys, } impl BlockCipher for KuznyechikEnc {} impl KeySizeUser for KuznyechikEnc { type KeySize = KeySize; } impl BlockSizeUser for KuznyechikEnc { type BlockSize = BlockSize; } impl KeyInit for KuznyechikEnc { fn new(key: &Key) -> Self { Self { keys: backends::expand(key), } } } impl BlockEncrypt for KuznyechikEnc { fn encrypt_with_backend(&self, f: impl BlockClosure) { f.call(&mut EncBackend(&self.keys)); } } impl fmt::Debug for KuznyechikEnc { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { f.write_str("KuznyechikEnc { ... }") } } impl AlgorithmName for KuznyechikEnc { fn write_alg_name(f: &mut fmt::Formatter<'_>) -> fmt::Result { f.write_str("Kuznyechik") } } #[cfg(feature = "zeroize")] #[cfg_attr(docsrs, doc(cfg(feature = "zeroize")))] impl Drop for KuznyechikEnc { fn drop(&mut self) { self.keys.iter_mut().for_each(|key| key.zeroize()); } } #[cfg(feature = "zeroize")] #[cfg_attr(docsrs, doc(cfg(feature = "zeroize")))] impl ZeroizeOnDrop for KuznyechikEnc {} /// Kuznyechik (GOST R 34.12-2015) block cipher (decrypt-only) #[derive(Clone)] pub struct KuznyechikDec { keys: RoundKeys, } impl BlockCipher for KuznyechikDec {} impl KeySizeUser for KuznyechikDec { type KeySize = KeySize; } impl BlockSizeUser for KuznyechikDec { type BlockSize = BlockSize; } impl KeyInit for KuznyechikDec { fn new(key: &Key) -> Self { Self { keys: backends::expand(key), } } } impl From for KuznyechikDec { #[inline] fn from(enc: KuznyechikEnc) -> KuznyechikDec { Self { keys: enc.keys } } } impl From<&KuznyechikEnc> for KuznyechikDec { #[inline] fn from(enc: &KuznyechikEnc) -> KuznyechikDec { Self { keys: enc.keys.clone(), } } } impl BlockDecrypt for KuznyechikDec { fn decrypt_with_backend(&self, f: impl BlockClosure) { f.call(&mut DecBackend(&self.keys)); } } impl fmt::Debug for KuznyechikDec { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { f.write_str("KuznyechikDec { ... }") } } impl AlgorithmName for KuznyechikDec { fn write_alg_name(f: &mut fmt::Formatter<'_>) -> fmt::Result { f.write_str("Kuznyechik") } } #[cfg(feature = "zeroize")] #[cfg_attr(docsrs, doc(cfg(feature = "zeroize")))] impl Drop for KuznyechikDec { fn drop(&mut self) { self.keys.iter_mut().for_each(|key| key.zeroize()); } } #[cfg(feature = "zeroize")] #[cfg_attr(docsrs, doc(cfg(feature = "zeroize")))] impl ZeroizeOnDrop for KuznyechikDec {} kuznyechik-0.8.2/src/sse2/backends.rs000064400000000000000000000210231046102023000156000ustar 00000000000000use super::consts::{Table, DEC_TABLE, ENC_TABLE, RKEY_GEN}; use crate::{ consts::{P, P_INV}, Block, Key, }; use cipher::{ consts::{U16, U4}, inout::InOut, typenum::Unsigned, BlockBackend, BlockSizeUser, ParBlocks, ParBlocksSizeUser, }; use core::arch::x86_64::*; pub(super) type RoundKeys = [__m128i; 10]; type ParBlocksSize = U4; #[rustfmt::skip] macro_rules! unroll_par { ($var:ident, $body:block) => { { let $var: usize = 0; $body; } { let $var: usize = 1; $body; } { let $var: usize = 2; $body; } { let $var: usize = 3; $body; } }; } #[inline(always)] unsafe fn sub_bytes(block: __m128i, sbox: &[u8; 256]) -> __m128i { let t0 = _mm_extract_epi16(block, 0) as u16; let t1 = _mm_extract_epi16(block, 1) as u16; let t2 = _mm_extract_epi16(block, 2) as u16; let t3 = _mm_extract_epi16(block, 3) as u16; let t4 = _mm_extract_epi16(block, 4) as u16; let t5 = _mm_extract_epi16(block, 5) as u16; let t6 = _mm_extract_epi16(block, 6) as u16; let t7 = _mm_extract_epi16(block, 7) as u16; _mm_set_epi8( sbox[(t7 >> 8) as usize] as i8, sbox[(t7 & 0xFF) as usize] as i8, sbox[(t6 >> 8) as usize] as i8, sbox[(t6 & 0xFF) as usize] as i8, sbox[(t5 >> 8) as usize] as i8, sbox[(t5 & 0xFF) as usize] as i8, sbox[(t4 >> 8) as usize] as i8, sbox[(t4 & 0xFF) as usize] as i8, sbox[(t3 >> 8) as usize] as i8, sbox[(t3 & 0xFF) as usize] as i8, sbox[(t2 >> 8) as usize] as i8, sbox[(t2 & 0xFF) as usize] as i8, sbox[(t1 >> 8) as usize] as i8, sbox[(t1 & 0xFF) as usize] as i8, sbox[(t0 >> 8) as usize] as i8, sbox[(t0 & 0xFF) as usize] as i8, ) } #[inline(always)] unsafe fn transform(block: __m128i, table: &Table) -> __m128i { macro_rules! get { ($table:expr, $ind:expr, $i:expr) => {{ let idx = _mm_extract_epi16($ind, $i) as u16 as usize; let p = &($table.0[idx]) as *const u8 as *const __m128i; // correct alignment of `p` is guaranteed since offset values // are shifted by 4 bits left and the table is aligned to 16 bytes debug_assert_eq!(p as usize % 16, 0); _mm_load_si128(p) }}; } macro_rules! xor_get { ($val:expr, $table:expr, $ind:expr, $i:expr) => { $val = _mm_xor_si128($val, get!($table, $ind, $i)); }; } let ind = _mm_set_epi64x(0x0f0e0d0c0b0a0908, 0x0706050403020100); let lind = _mm_slli_epi16(_mm_unpacklo_epi8(block, ind), 4); let mut lt = get!(table, lind, 0); xor_get!(lt, table, lind, 1); xor_get!(lt, table, lind, 2); xor_get!(lt, table, lind, 3); xor_get!(lt, table, lind, 4); xor_get!(lt, table, lind, 5); xor_get!(lt, table, lind, 6); xor_get!(lt, table, lind, 7); let rind = _mm_slli_epi16(_mm_unpackhi_epi8(block, ind), 4); let mut rt = get!(table, rind, 0); xor_get!(rt, table, rind, 1); xor_get!(rt, table, rind, 2); xor_get!(rt, table, rind, 3); xor_get!(rt, table, rind, 4); xor_get!(rt, table, rind, 5); xor_get!(rt, table, rind, 6); xor_get!(rt, table, rind, 7); _mm_xor_si128(lt, rt) } pub(super) fn expand_enc_keys(key: &Key) -> RoundKeys { macro_rules! next_const { ($i:expr) => {{ let p = RKEY_GEN.0.as_ptr() as *const __m128i; // correct alignment of `p` is guaranteed since the table // is aligned to 16 bytes let p = p.add($i); debug_assert_eq!(p as usize % 16, 0); $i += 1; _mm_load_si128(p) }}; } unsafe { let mut enc_keys = [_mm_setzero_si128(); 10]; let pk: *const __m128i = key.as_ptr() as *const __m128i; let mut k1 = _mm_loadu_si128(pk); let mut k2 = _mm_loadu_si128(pk.add(1)); enc_keys[0] = k1; enc_keys[1] = k2; let mut cidx = 0; for i in 1..5 { for _ in 0..4 { let mut t = _mm_xor_si128(k1, next_const!(cidx)); t = transform(t, &ENC_TABLE); k2 = _mm_xor_si128(k2, t); let mut t = _mm_xor_si128(k2, next_const!(cidx)); t = transform(t, &ENC_TABLE); k1 = _mm_xor_si128(k1, t); } enc_keys[2 * i] = k1; enc_keys[2 * i + 1] = k2; } enc_keys } } pub(super) fn inv_enc_keys(enc_keys: &RoundKeys) -> RoundKeys { unsafe { let mut dec_keys = [_mm_setzero_si128(); 10]; dec_keys[0] = enc_keys[9]; for i in 1..9 { let k = sub_bytes(enc_keys[i], &P); dec_keys[9 - i] = transform(k, &DEC_TABLE); } dec_keys[9] = enc_keys[0]; dec_keys } } pub(crate) struct EncBackend<'a>(pub(crate) &'a RoundKeys); impl<'a> BlockSizeUser for EncBackend<'a> { type BlockSize = U16; } impl<'a> ParBlocksSizeUser for EncBackend<'a> { type ParBlocksSize = ParBlocksSize; } impl<'a> BlockBackend for EncBackend<'a> { #[inline] fn proc_block(&mut self, block: InOut<'_, '_, Block>) { let k = self.0; unsafe { let (in_ptr, out_ptr) = block.into_raw(); let mut b = _mm_loadu_si128(in_ptr as *const __m128i); for i in 0..9 { b = _mm_xor_si128(b, k[i]); b = transform(b, &ENC_TABLE); } b = _mm_xor_si128(b, k[9]); _mm_storeu_si128(out_ptr as *mut __m128i, b); } } #[inline] fn proc_par_blocks(&mut self, blocks: InOut<'_, '_, ParBlocks>) { let k = self.0; unsafe { let (in_ptr, out_ptr) = blocks.into_raw(); let in_ptr = in_ptr as *mut __m128i; let out_ptr = out_ptr as *mut __m128i; let mut blocks = [_mm_setzero_si128(); ParBlocksSize::USIZE]; unroll_par! { i, { blocks[i] = _mm_loadu_si128(in_ptr.add(i)); } }; for i in 0..9 { unroll_par!(j, { let t = _mm_xor_si128(blocks[j], k[i]); blocks[j] = transform(t, &ENC_TABLE); }); } unroll_par! { i, { let t = _mm_xor_si128(blocks[i], k[9]); _mm_storeu_si128(out_ptr.add(i), t); } }; } } } pub(crate) struct DecBackend<'a>(pub(crate) &'a RoundKeys); impl<'a> BlockSizeUser for DecBackend<'a> { type BlockSize = U16; } impl<'a> ParBlocksSizeUser for DecBackend<'a> { type ParBlocksSize = ParBlocksSize; } impl<'a> BlockBackend for DecBackend<'a> { #[inline] fn proc_block(&mut self, block: InOut<'_, '_, Block>) { let k = self.0; unsafe { let (in_ptr, out_ptr) = block.into_raw(); let mut b = _mm_loadu_si128(in_ptr as *const __m128i); b = _mm_xor_si128(b, k[0]); b = sub_bytes(b, &P); b = transform(b, &DEC_TABLE); for i in 1..9 { b = transform(b, &DEC_TABLE); b = _mm_xor_si128(b, k[i]); } b = sub_bytes(b, &P_INV); b = _mm_xor_si128(b, k[9]); _mm_storeu_si128(out_ptr as *mut __m128i, b) } } #[inline] fn proc_par_blocks(&mut self, blocks: InOut<'_, '_, ParBlocks>) { let k = self.0; unsafe { let (in_ptr, out_ptr) = blocks.into_raw(); let in_ptr = in_ptr as *mut __m128i; let out_ptr = out_ptr as *mut __m128i; let mut blocks = [_mm_setzero_si128(); ParBlocksSize::USIZE]; unroll_par! { i, { blocks[i] = _mm_loadu_si128(in_ptr.add(i)); } }; unroll_par! { i, { let t = _mm_xor_si128(blocks[i], k[0]); let t = sub_bytes(t, &P); blocks[i] = transform(t, &DEC_TABLE); } } for i in 1..9 { unroll_par! { j, { let t = transform(blocks[j], &DEC_TABLE); blocks[j] = _mm_xor_si128(t, k[i]); } } } unroll_par! { i, { let t = sub_bytes(blocks[i], &P_INV); let t2 = _mm_xor_si128(t, k[9]); _mm_storeu_si128(out_ptr.add(i), t2) } } } } } kuznyechik-0.8.2/src/sse2/consts.rs000064400000000000000000000005121046102023000153370ustar 00000000000000#[repr(align(16))] pub struct Align16(pub T); pub type Table = Align16<[u8; 16 * 4096]>; pub static ENC_TABLE: Table = Align16(*include_bytes!("enc_table.bin")); pub static DEC_TABLE: Table = Align16(*include_bytes!("dec_table.bin")); pub static RKEY_GEN: Align16<[u8; 16 * 32]> = Align16(*include_bytes!("rkey_gen.bin")); kuznyechik-0.8.2/src/sse2/dec_table.bin000064400000000000000000002000001046102023000160460ustar 00000000000000OY;yS-l*72ÎɐXD'e`nY(Qh4)vMQ0z҆@_h7tq89abLv+]ÿcxDTTG q}r@ Xx 񤺹ew{ ,:vKx9BT(\SUF_ZR,rP~W쨢K@`=R,EiyxO%!y.d.1"gEQO0i ش%W9uZZT) nkhR1A&`;gπУn<"*jVpP;ǝ~=iC 4X:Z!/YONI=/+.̽X ?g,}-L;%G ^g `d-sr(;2+m sR5Ip%̬'I}PcD7~1PI-7]e3v$_}HnxE2 LBx҂qT `˓XI%Al)@V>"FasU:sR!wzYuJ$\bD } ` sJpoc) Ir6-ǘ͝sĮ]wA&|z`P/u\ 0o2깴SiGH,I4c5wկ2[X z M8bZfV(_Y:pH\}*i[9 yts,<yLR]gǗ3 գmN1I!TF= djmքRq obWNAzGp+dCLtOQ#L'f,:AwOd$nq{zDޱ3K[k6esaCFmpI,*[_]$F_cÚ P&vdV=ګ!* .LȬzw7rM[84+S@q'>!?Z9⒧/]d Fh*?oa@̑]%&CV_E{&v#oôR{Bfٜz^V e\?[yMӜWC{cUl1'=8mSyĈ_ !/XU1 Op)M~>=0(aiQۃ 5fUg!Av#ע8uj̪Bh JW%)<|ʿ!K^iT~e,µΏOv?#(9N%|Bխ~вO FԾ9b nVsLjNWA"KӾ0m74B^e;*ɫbR'r |RSf Ϣh"C&ex$KoƼ4 4K<+a5 +8(WtJRx*Ya6WBS&2S0cXG5}L䂭f zdʷJ t~zO- 2{xEp3;/P Qymokn%,!phVQS hY}#j_iJB\=$69;Q7YyO=H"uo_ 2 GhT-)3Qsu4<iBc^Aj9o Ǚ̩qȉ[GvǦ"#Tl3e%H:?=E XkTM6Ldg`uƓ;Wwy|~g YFp<ÅFP8rJi{:Dk(G`\N:*"j4{w@a1hx5MV}·,~Z8yc~<r# 7|_>ĹE4Iy6ױ/Fe"8vl YDdE+0j,I ME3Ӹ3cχ~HKd<qW&w)Ͽ.|0hCQbS6YHemC_wY.֨Uliڝ_b BqzfCa]AxA¼0.t|'wT0tTD1h|Kkh8$.ߛb?9j G>[t& H܋;Q6"2Ci5 ţsyÛfj쯈lݵO\`E`計 bk!/ \q`І?$Ħ2l ʏڷomo-7iۇ椡BA꟔eH7`8%5ap=tN݈Kx:Fk@-n0f27R㡉(uɴ)ځ=ͼW%>V.9WMߩC&Df>~dШZxgڋ}L5| >Z?nu#8G!qlP-eu [j;HQ4*NiR35vD6ƥ.0K0B{UIk\O;t րZ)`}!3o{D?-cn/)p4 1ȱZrs$St]kۿ-d8 a ]}!ܬŘԫ.9:V`gX$a'Պ)feKJ6[N]'?3;d_4.ߴk&rA3=2|PS^и @ .1,Md@ {Э@Go[6zw>^H`6FgQ15K u*+EMoߤ#⩇(c7k>g1rCU^Rx "rlu.?mi]{OөzU;<F,uEC7NG-Wj]|^qIU@+s {#ѨGf;["cq뇉>ed>(N~fv/h\u =WV/$0#3otmc )$SkwU}FQ<ϘW#UЀM݉LjNʽwܲPPƍn$ֽ|b U僰JR|D+\~"һ\v_2by Nܓ$7< N&25ׂkBG)Pxٻ{^ۖZwk"BTprK?($npFİmSj%4/ b #Mf-)9(CuZȯp@£$ź2|&l: ΢bjU TGV+ʐ TNÁ5Ѭ ,l/DL HgOh@@A!(**'V6YL.JQ;Ω^1HIxtz6L^U}>w:RL2ͽ{Dq# 9ح Fx}tCh,<5{@9=FrdQNy@RNq&kxa LS`'l.vQTo#G>ҝ"=2Re NĜ`i|e$@HxM6BAւ連ϏF8/pPv$SsaK23dœ+{7ö,> ItDOF_9k%ob@:H4.0rs',X%ݲ:׭ 54Z`dd Y♏Fkrz&ȢK06;ɤQ*MLj#92b 'u ~)'o`"Eəy~}նݼ:$Vs_ߍ$oUn[\0-` (N#7X.Jytk5mo +u5N\E].w PVX+V/ ߴCLء1JM Ily#<:ȕI鯛>^pA^WH<©d,fGVsnE 8HQ_7q]d{ dlb3` &e=u 9 }'q&* vgH(+, ĴeDNia͓-j%!~'eʖiU,8d?=nTa6heK[S2xiCuKP z_ moǷ]Nr` j$ X]CS!HkAwc @G7BWm+Y.Sc~G@YK/?: ÙQމmۄ(u[aWbŹPwxҮeY5ч5?F|BafϘh W<gSF]cUI\/%;$m`Z !PuFn&/r>)q=7 fH+iZs:kA]v. [&kqtւ\@LcOMAͪ)3Zf{K˓+=uP,5®ēI.\k9t 5C^~gu3t6")9qb]W/fiC4B L^|R'z6n}@̐r(pPꖕhS|UksJ2Q7 &thy4sċ <2;[VWUB56a] ƣ#k^EJOx:y;9m8(uK4ʈgqApN!B?‰[0*6"E#|t.[/ "8E 8ԥoM6 OL.ԝ: V/p4)e5jf4:F! Nfe*&{ LEi5 D%Si(^ep!yC4aW,h 6@0t9٥y3$cؽÙ| I)ɇЯ]cJ,pI~ p(=6YZ8JB}3S[pj^{((?-3zmBNvn3;vU@<ǎmTV1'ދ#I|~%AڊKɝy_7Rx^wăw&lE@% fLzr]fv 骀xMA&T繾"X0iJPBucߔ9AX;@l >`=K!zܨ,Q0UlXSi85/܊=Cע!';7ő?`E8bgr`ҔgW8'J; Gwז*+yQ noKŐ5{s%3T 捾}H*;.ܶxy|Dίl؈2׌Zn3>RsE(C&RQb7* RM :t R'žg ]زΎv~7q7"lM/,~g4Ӆ!dvjl/^X%m kheB{j+8Jf seu{LH%J;_AQZ+]wY!{0F1 9K\"[J gOE汊!P3L4A{Uݞv}G#91(_Csdm۴f|&$tB K1TL?ғjHqIl"lYgG kp;?uf\I1}r $A1 Sw[3>?p~ZBamEzk)YEF1-sRpQ4[h+.X ?N76L>TI+" lD1R!r32o#Fr'3өbPwߒOg $<ǃXmƒ]GDɌ o\S4a?{X& [\^O+DpR#HOiu-3dhĄ(ox* ⯐cW#)D8yO1)QB$7=nl|V"ϾT'M905Xxj.uD؈bBfdUM |DQ+#}睙c-|2n4bBTzч,Ec1шbN&)MWO}QJz}:k)_ҫ"Yf(; dWj%PgZ9CT ~=-Mr#8(e;Cѻuǻş6 bZ "Q'hG%V\8~ٿq T6v\{TG:/mJ(Z_*@IǑ\yߍvw%=ZHU=m _ZٿKGʖ5(ҋ-*Tl|?9߮Jyir͓s3zZ2 .cgʲ0jqzB{XG-Y`ذ0ۿqot$ٲ2˟wUO;| Gc;t^ڔPnrހ*_,*ahY~%LO->|71!*=OqHnzY*DzxAwUIJhҚG50毠_c人gZ!|gގwdn)NnnnaD^ivvvzXo>XY^`FIY !+p x2xxէ6C1m_GYkf=k+,_,,k,SSS-Ur(:-2QjAuE+@@@,ݑ Dz|4ˤ4>2 ݧ-ϳȕR*`D؛Q~Rfqvz& g OEGjg MhU[h] t~0"lll?nR(fj8///DB[Nʩ_HBr5=v mrC);- tpLy{p R222$MB؟M"O%_`ޫ3_h9&i%i=e`ð54eee8.: }Z:F@a=&ҏ⏏ < N赴务šg,*av]B uuucy}}}Z]S{T]sUss{ x[6B66V7)3KEZsE`W``:R8&]mRݝܝ8v|KvYGoGGC\27CY555ӏQ4odт0ꊗV%06uXy;m-y4j\Ձ(ppp1􍦽 x ׁIKXTMKxUԀ3D텳zL_$fcQilcNHgEjnzJzzh-؛)ɱmVYŭ###ةϾMc9;9Ky:FC1;@6V@S[[[~p1~e$d17 dddHs /ȍȐ9y^{bkr(#>a>>E*qzkl˩***˸L^ -T,!/緟dil_u bRRRzD#?]??Ojaqz{v{{Gfӻgb7=%==v4lSY?>;#SXIS000)NAu)udfffq#}?w F8?ЖW)WW ry*@(rJ?6TmTTU=8ZsJ+ȐF|||oT<[eo y.@LLLF}񔲁F'q7~77|"/ՓvF :?EM*w|~􄅄*]D߱{+++6c1)ݍpZkG+6HTP.䔥~Clڨz2$posʪnj 9 6WS܂3֑PX2333mWV̫n /D0Pj jjeh e$|$$dG "GqooohNO~EDSۅ k%D5"!'e$!8-~~~ $w RQQQI >{?IezBBB~" 橩W19j`&ɾù\1i A1AHbnA.7$b9)ؓw%dHૉ k(c,Gרx+޻vEB.O.IzqPe2D,J*faZ[OgpsOVVVlƫbRyCrȼ,:^TsڞK<'ڱ =Ⱦ=} M[+d 9FSFFgqs]4KqؙEEE'Lwh'_P2#XI5LaKKK [(tg\n;>HurR;Hq-qqƢҝ8sPCsŶ={JM(FJXCTU¿888zSK Kj oh! y<:/~%@%%u:n*قu}ktttC<1Iºf4:44oᠡ>?OUػeU75U[M4MMGtQs!tcR  J9w7ޠ21w1/Ί 9/k0kkW\c*W;Ô!{UMiHii#3uk3L@ WC & <>:Fg58>p^6^^}Wf}RP}Ebт;bGcccIA3;= ꯤ9%`ahthh|~%EP]zڢoPP"b+ُ0@+.b^_kr^VUQUUv2;3y A`4'Y?Tîgx%ҐFfW&f_ __uR\0@x3ST)y(`Hw a-lmuPPP'{$5{(3ZZZLY{h豢$7B"4 '-c--}&͐&t999H@,ќHըA XB)3^; MIe'膊3ɪն6ZCCC5Mw.?`<2/Ko 2yn @Wn[%Ǎ^#%#'Ce[).[\III5m4优$Ve__ŗ |B9Ea+9R f,.9fV$+_:>.T3*(eJGb n5n̨W1? K:h,W6os3=T`tT-+Wk(]8"}3]\~nTmZ<~t<& 0ʑrۤW>=$i8L̢k bI{Ms4ϖIs^f:ε_ y83$E}[Vt|7\ g7fTPʔM灵4X}Ni}*[%YI1H HDs= +YڻxO4xLݭɩl/!Uf<$3˒rAY~v4U:C9U}6sj [|..З䩖7{4ƴ&fQ{P{ 1şHm)P`mҌRJ%^AaoŹ^H$Kr{jdwTj>M !m eӾEh][̑t_ѾpfHG^֐>ր@G3z5CG>Ck}~w\Ex<tis?[&Èz Vw;h1S]Ke)֭ܰkBl|{胘QIT| u|z=Hp[/փ/$Ē93ܱj(_[C.I݌qU'5 2*5\OǤ ]i_XjF)eTO e.z.t:T%`mWxDmcШ)qRL/ZLбU/\ikHSk@ =Gx+Q@ݵFE R{oք_k*0:*SL EӵE4%l&nxP/_VT~_ X!EŏFU#I {a^*q-4(cbraiF )a%w|H}F'TEJ'u\mh19#ژ>$0̀-uS-BݼH%9CH" 7A6kb6{|i/[^-c?: =(KY|K҃i為zoeZV-)UQwѽXRCr-z8yzY"< `^1Gصbّ}~A tX>aM5SkK Ggy$GJ3"4![[`YZ꽃#Zˍy @՟ zgItq[nMYt -!nuRm.ơ?]@`)r'~$$RiN;y #NjL {"V~} 5vwZ5pYDx,ak4"e0FޓC ZRݶ@W)2*+Eړ-Ӂh hXq瓭3i3+ܚcZB~B0/BGvlHߡv5v7bו\yە8N,_'| 2C1Dj]5 +j :'+鲈 b±bPIπoKf/3gXHUl]χV L 0 9ԗ%@X#62zP2Qg Ƿ[ \\tm")cL0j)CuESmL3VتJ Eh$B׳_ (0wuH>fDF?!KW!;a6$; ;w{1R>GiՒA]IQIr9?db,/;7,Ժj@nl Căl3A);JErD"gVtNV#,$6ϣ}+&W嫛y\NKZpo2EsSya?('̐?m7sdXtd{#m鸁upVS:7:_hw"AN"25azJ!&^ۀ@۾PxB!q^TKZ7;hP(ƣhNpbƁ=>zƌK@JZgsU񠟾QΞDpzMeil(TwX(Wʽ5+2Rv㙔D,5O2L,:2{惪>!ãl½~PN[~mnm F#axރ1n[?v/#g Ǚ{`+?;pAxisAQY2n^K XE\gr̬HV ßxȨL& #v!&#b1̟}oa0yE0rnrCNmɈ}Oky:Q^QB̲+*? r,5UxvF1lfuߔ&YY;2NH0Zw05G%e+hJ4*PCw#wO*řbeܶ~/ =S%S^x/@Itp /lZ &I%zni=ip䷸}Ij0(>n/ċY{gvg9YM'x5&!pGԥʏ`o@DRƘ`ReU [='`?NBrgU~x'$|-FkL _(i6R' RޛDLj`udčPdL0pp۽w89/eeh]WR WNGapZO^ZHu 35ΊϢb?`E`\ pr|qqKUL!3 !}ĭ,MrҊrh7b**ըwS^`ع2fzkUItr))pKo=$kжƪL6hl)) Nuvd?V?X[88kDt5yyɉ'p',*wgysY @e& \$q7Bx~iBhA+r^к.@ei--(Rl{U Fhh[cU&c|4yMIKI,HKB8"CTBfBKX99!gknRq2{ J!t]]6T-22 K٠ȵ`Ao up ,p":pFm ٶ\{0iۅ[.Av A!T<\?Ռ!mu4yuFS*91^@2ˮ𪯇{:Sh??8^ |qPZ)M6hM#A#__Pѿ6뵤*md*?m̆%%pLj:^gl^ '{P^#jV_bsDRdnfvVnپN⩴i*[/J×J7.//t9NAB(G5 c5N4)'Iˆ<؟mk]@xdxK`P0[HUjj}k`k$5Y")2Q73Q;+<,eׇզYǶY6XX߹>?=,݅8 _'qZbq@)PV*xVjz_}SoPYj%j Gl3=ZˤT]%%^E-n{eueaD## :=\^^k_sr(/|^mmHwqwzlFԬ <2<0c-nn{W{ݱ7Ř (C(9 B"GZZP2-hj\0❝ 1r/1gQ~qn*zYkE114 _.xgg__CLҝY33uR,CCvrωM[lcv$)v3~Z ,¾w,O_..>O!N=660 Uiد ;g.cN.Lu 6ㄋ6[8v.FF݁t]z#_4拋Xi5.it~a\\|5%B).LdEj!Dg<=d.ZOQo(gb^Cbu~I6~~p;;?-za_o{rPfe}f)x|߫ xz \vU\ƜM\'llp,mmEBq躺l>,GZ]#M}}b7R7Ssda֫aե:bbhKK\1=T͹¹Jؾ(HH@̜W$:ϕ *a&Ds߫b%((mA1֠ }|~zXJPPڂ]bo U(>/X$ګ=6WaaSGVGT ݠwźEPQ,,V^||(3p3< >{y1)~ Ⱦt8aЖ,]Yb?Ikk7oC=om͐/F'n;en$}}[+o*iuZiynISPkPr@ +OO%s'ƙ]w]њC"o& 5M7¦gauuw'&VsyOvvBI;dUU#tx}?D}d_G;;H!Rfɴ+UXGX/ MMc3wwxa+sk!NN@'̩Q# |DG|rg p> DTN|- ~ 2I bezdzٶii%gg`;n䀀lE.YE;@EQQȇȦ^77NK܋vH ::}aqzHcc"OlOY3yGKjQN&4NXTy@~J#oᨯJJ)يZ%[>>F8fCLL{VY@UM}8&f>">V9wV1=2J=[NOF*a]%l_w -\OvӀWn""ݠN]$:SӊS{[Qrr=B m c9>tt=Q!:3&&+ ˘ ae?%Z$!TTi*9 TMꇀ~"t$$ fN j44̕Т>U _$$ӑ]Ɠ 34Bt;4p%BB<[QHn"! FY /eU?==qc{uA@&X f+U D:D~䌅uCi;+_ ϑNajj >%X "==Ѳ;Js}$1 .Cc6\Y}BRBTS~YhsP A> !-2pK I 0b́),zִQdYYveqxN %-օIv)vX8޹x,#RCCVˋtKԞ׾#=6殀`ݯ5#tEuQua,TcPP/AIGIG|JO@DzzSΥ 'D@55*V?4ئ(+x"Gߪ^#2_2&c5,P?U ӅQZ~D;cy,)X:p[C;8>؇1 |8=BZd閹g@c`ęY3R_]QHFbte( ¨*sgfZ1_.=a@ڽ&M%%pXF0?=q-[ U՗0A1cig>, I8i_mz_|lo`hMX\_6DK]IiS$jrr A avV,]-ԤPp ίɽV= 9 Фb5ƳE:x&[&5A7 r~Dy~I9#`~zhD3 Qq+I+pt'H᷎@ vhmj$9J QZЧi"6zF5QBнN(ˣ刣+rHPR| iOi!h'A D= wȑwd셶ءwƺ+MZ1_Zp$z!)%M3HBR˫JS""RuWn{ZݡYUE |;';B#8Mȿ.3vLT̎FIwL ,h͕O/OHNWl=#p pw7z3?Pö9LƱMjX~eɭzaGxgC ; }45zSOZɳ7ˋdҸr^yUf`X=-^B;̻656Zqm2wv[ljl_[H} J Rsh?|\Qf&aC[8ߥͶ~lJ] 77kM{ê?/veNvgyk  m]opws]sQ!nU0UgC" MťZ&4 ƽ׍h=]A:qҚB$hncwkV||󪞁jk;HW'4 K:~:UL†}p}eqn2u垭L8Ŀ`%kP4@{@o/!mXfWL73븕R S.y!yҦݟHԭa2vHb{\{Wt4N BX"XbdS ]%-<YUA/ΆrUO5԰Q$Q-mr c{c4CMiMU)7d-ꏱsW@0q:v۝Nh.@ict3tOV&b?Uwɯs9ks[?6%Gu0Ԍ (SJ ֚E~,3^ʜ|!t03%{[T" ΀%0KAfAc*Q$zʬEH6G֬y2i\9p/`NNU> f' fWV8)4)~ EdڍDlsj4y°`5ʊִd֫`()g2'CWLMW6f'[ #~#]B@o9n?"?D2yfYx,xNɆk~S>6Qr}f=EeVVle SN<@FFvRM*Ft_YJ `OC!̋{YwpRhRːlc{Oo33VKqEݿaX0eEGkZ2k0DT&T?IGNm2mEB[d3KX?}Q! w7^ L5#. d!7(k<o0Ʋ0,j:\J>yj1Fms}O/|)N@^9b$(q)>Հ&~mA\)TxTq js9pVuk o< )\6/|-a$u&$"ȿW59JZ9li^ZgHLHjF\ %Bn<9'1k&m.l.Ҽ3- Geΐ1C2_eϟ]*\’'OFw5.%ڗ ^=R([[.80^'A'X?t |q<ECb6Bo;&`*fG#GJ>MDi ꂀWLŻM } 5g8+hY%ؔߺnnxrNbKu8L\Y\v"p|3sb<<'CԮm}ޕS;uQg쭬8# ںKGjWooPRdӓN&4 >Ju(hUF}p7SS yc*E^Eoz€wLۚ+Js1JK(.ME:947H47e[THW {N<$u&e ݡ9*u>k:~axa|ȃ+vI1km1 KeW^,(n=f~5E!VD}ǼFd]dqS6*CoPê/I$z/1r@cxHIL}qTc]--=q„{<:_y'ro!,T,Z i"*y`eeS#/VᛯgbbۻR1lGbTnK/`/k _mFzln8: $葺G?}ס|J.*<*$#1D:{䁃aTt+E- xDn9PP?rFՉ]]04v,.]GpP\t%r-hnhOnʶY+mPֿW x.`O`}بѻm*ztUg7yF^UPt]©Tε̴b$W\%2P%ZK" r*ѫ*XY^( 30,gg4"ɞوRt(8gw+{/\Bs# /(:a;)nwAf餓#:KlTx{B Cf)6_ KK/&2>dx{V[;iQHA۝y[Bn3%'0eL6Z@HB;.ʜgYl9kK +YMUk=3}:;R (ڬ1oΆ\7plD`Bla@g* ʥeЧ&R;E}@s:NE; :O3R@Un v8Q?.t,"ݩfZb`?T30-O{5Eh_󴀐Qv\2W)Kq2࿺H?BIo fJN7` -$ݘlmVgC.z fit86"v0!G@oi`S a)zFrkx^>ӗp94~\C9cT/Wqi1'{`]=Z,Ŀp~Wc|?fxR|y-PX1j0\ *a~Zp]Vd}G":$@fjRFcI|v~#¿ AT2:,<3d>uG8 }~+Wu.nAZHWy|ۘy߱ IXMSy@8?Iʖ>BjZEώJEOPa*ό!S|&{( e)Urh0&4iӸO1^%0<_%ˉd5-1>RG;d?Pwb?-8_ϸ+yQlp|c4WwSapP*~VT2]”RL- $!eNM Lݎ,pǓDvĪ.HʲBk6`џUL_ h{eh)Ù u+AT] o#B馻[#VT酋]3'~+'68>GFtvw`lc L92h&s6C}iE Ou35N)#{ٹJZU 'Yhܦ4a*rzs/#C ޵Ԩ#:"V!$t7;DUB HHfy8Fnjv>WcLv!tY >IS7yIo>X% 1_0wS4[gn؈yAjx-`{J HQ! /_*DPsaoEhT [#:;eiJEUчȟj Zb ^Yrxt_8(r?JlwU{x`L٠9=gmqdkҫ=Y\:R}4}~|&/ 2cWnvf󻜀gW٘B7 .T4ϏQ+}PZjEĎ]+Ҕ a6S@(,O5.s[^CsO,Vk^Yr^wF%IM7J;3N@ Uoi@P& 6RZOqmnE\("G&t܏!Ru^'h{0[\܂C4Q0%sv!͓"Qit̜'s3O} u&6M c/olf9YM 7$b9 61R&jSvwfx_~Я_{Le'lUҊIɖo;x1^îd Ʋ(R0Ff\Ј4Y~lp S*4" : FK$ +p*|aJ;F:fNɤ`$̸!7 cۄ/Q | 45{džh`'J@lbU$eY_LM!Nfc1Ll2g qKŅKzCqbi] 2廠q("gmz5mhrA=[EBYDA[e5)#$3VNǨh#,']ݢV w:n)ob(+t*-}'e) Z+=ﳔMeq',mDtz伓s*OSWgՔn\2~גJ|~.I) uΧE\Ps1on 49̋p×sK\qW1!jRF:HE;fMbaoZ]DP jT"m(GяrÕF>XNy8<7LP6@R; ]PVaTZ#?pD螺LQC=r^؝kmȽsGr%_^(⭈Hx @?|AnTAwH }G eKDhq!<,m=r8aޠb_w+V:uF3)&\~'Ƕ{j06&ziu) g5ıjZ2]+ b1ѓּ-O"(5*xDNX$zy2 iI͵]kq#CV/ z<,9/TlcduK)¨F}d 9GSoPD1 x7XS*z$Ipľ"4V|J-b`m<:d9뫗~̭+Q*F&G8"dt;r[h'^{k%"'V,ē=݁ByM>AXn y 1Tow8"R&GBWԤQ tv̛F1^:xXFaB=-0|?%2)SaZĖ7iU}9:/S4p~J+l0IzCs% E#8 Y;tslj+$ڌT&ja8ME~^X<_P3ߒz3uEd9:"`T 2#f yU+]_"wǯG~5Wv_ $REՇK3Nq nK!5XȹdR!W3cKnPO&T) WBKI>H.SZ`$~ J]lF@XI.7 DE ?s$Yy֎p-B=C;UH̶C,h[('v=F\ȉBGfƫ;'06LR@y'C 8V]AHgmw[vtWxiޤ}LvygFMN7zD&hSwO#09VSы<ݎIO:{Na~Y&V8g7tvwQ|`- #6Z40B9%?<}X\١^8rQξ bT)2He; MكI̢BY+[4\> 7ƈRJaxAy[tMeLg, ì,c )UF"M&6רFۗ> ="CBr HQd 7zJi~Ox_'q%p'_5ܹS.k^O%!ĴJU@$\ *|l`DcxWF>0HIT(-j{`C"k/ݳ޵Q:=9*Qyr! ̖͜n{o*ܖBjSY,Uԛtc/?%Q\b}7팚n 4Lqk$2K=V{#yY] T#j{\ּ)JɌK WCz+:*72NebZ%˃ .d>9f 8h1J}1*N $wPy>I6A0o"&y1odm$ԯcx vW4֛x*_wvk6@┤H,dL]G1I-0=:؀"pgYP|.,<@ٳD ƞ6 sE*i|rsYAp4bi+Χ:S}jk#Eb!Ze`ztF kv/CψAqt"vx#˾)|La{bb(? ,REi[A5}v;Z} qJR$m2&e cuŽf 2jm!q k4x!-(|37t٥3`{L,!abupU4+LK=NfV.e,​,f8%^?0x*5~0)# |.#`~ՄuE3̥Zu_I@6]ʅJ'=3nk$}lW(B֕hGK}:"9ūH"ةSHy.|1Ij3 5[h-]eW=:0&deRz̃;:\ʷ7R,YdY.tcDr)%f M"cOonrձ .l2b}|n2O )79l당QwXȘW$O>Ŏ}5>YKC ^sU@T'"n/KƛWJ|IÓfp?e s%.HAoZ " {hem`;!rzr7V,[!<.mȦgY菥7kopEm(5-'* (5VѶh{/C/zTk lL9BȌQ_OI=rs2+5IN2u-5{,bVeL/:U $q(g;t#TndqAE4~cg,y.>ŪN3 sM8CDO=9 Z+dSQq@3NG/K~%SP;jZ 8BPGҪ#'r ="t^NTPja:^ ӆXSwPaGN1ؘarSۢ;KJҎY`kiُAlV^kG8HL2 ?Y8-IA< q?n`eaO6# U,$)ǩN4(/cWwCxU uΗ\㒂\ؽd>(41}n4XBx\Sβ9q݉nO0?BZ Pf 藀΢MDLe!(lmu@$]&ENMŒ\n~!:Vj)2#QgT0{ գ &)ob4s˦uYJ\'} Dv<BHRMٜ7 0hPA^Ʈ˪^ODQb|Nңj:;CQzYd.jw10o{&6غ^y"tzrT ϻ_'ǚ M#iaPZ;DC^t@HogQyAPLRh%iG UͲ JwvԴtԝ6&R!dFɢΙ•JcI! ڣ 58+;!>eDۮ)l'W\ Utx#Q7ia(.qܿi$#gGjM]8@~n~ L{ 780}xb%>ѡˍź0Dc8Djkx lIe3m xYARj>*L/1:{i.=ON8[܃I)z'L9VvIB|׈uΡ20/}g֠9ENZvP]w驁62jpտYFn‹5P\ ⩡{?_p: wbt\z K!1 \v)5|N&gLuY_kr5c2 9i*Ks{XG)y %.7_ B~g*q񟵸Ls]Cڃ4r5I젺 ld?$THXUvo*wfsŇ!YP@ڌ,$ MvOH'%"L^X{ZDc֪>n`E- OXk\o"#4s!^4:nr}Rǫ ! m6erݒWm_d`qu^oҹH &F{Ύi2Ć>igwFMx{_!?jyJC\M& mDS^E"LC[rGv\=FO2({f/"@|T3Y^*$,Z^;_֨NʡԺyL27?h&t<~1Tpo7|ZK^γ+ 0=2,CmcAO?ިxbCeګ#acS7uOpy*p/yfe\cT!RXshUr>'YK{KRd8h[3Ye݂ 0#.dK|W錚RΓ8^x})N{o*d:Z,}G5}PGVOt,Edİ[GP që'F-s;(L8@ZV6vD܌K(;@ VPkqYymɝ}Y;z2op{_7R5N+1X67 Ѭ+`f?aWc9|)gBQ/<ߤ'[}M)c24B]̮MSAvHX_#o=&K,,G۔#B3 Tt-ʹX(ٟ#Nؠ`^s:6C0yj3,(i'v&| z-+_;HC"nF '9)=hd< 9Q"A2ȡ~Hk L"0N:UF\ ]g_# Q~NJytbn۳$f_|DZ4t%ӹ)C5Ϭ:0RD-<-I'Ea8v8Ml3j%qth=: |Wޥc4˷Ѵ#hJ<> _?H*8]B9-xLPH-g[&qW.tIl`ז&_V'`zdKXU aF%Дx|o rTY$(>DdsSJeGRj]&tQS3Ο>"u-FT7ҭJǥ6kf5:4>oFUm)"EsPHP|;o$ rZ4%]ZG$oLغzvg~M5B0G lmHOd bB "T P!uH:̾9hL}պ֭R'&ͩs+(TWᢵ aGMAhHZsR$`8 AOV&9~;.##?H , p@;s>=$d.T=u4 mF ~CXqc0Q3~OxЪs#˔ RÚ(폇{W V$\21ݐt[r3.uɧu?V'm%w:172yY/$Ą2@}D*UB[SNrGR zEx)Ir0 |a+< UI3`>AZNъpǻ'kng}=&U7\zh˯!<& ($wqas k٩W=?dP^]wD#ʳ+.cpiof[J荵zt?euc(Q0m8EZlz 5e8WJɼm\OlQw׫n-8ScU19% 9ng!/gyBbE-/UkT'ⅲ+ڏQ0EY4^g)k@֓9*͑? 1 4i֛jjJKM1re~l]tM=;? GNL,"\Uƍ/qr]-uN6bZH2L_l;d= mKl䬆_I]m2UΨr0A f'`8mёwȗp Rfn[=CT`9xC#Ш^hLrc {} Z['U^iǡ̳H97.JY 8rhU:fN;cp-~PTDaqdQR?@"7Ik_Z1[dk%vȟz0G4>Џ2$XW ~LQ}p[ Zl1.ͅ׋*׉ d8_)`37@OUNA Q_EגgT9|}c3OS0g!K>)kBrcn\{p1ǃrqF=c1}їqEa l)޺ ZR[fy'^AO3\פ:y ~Dn?q(b*"x\~ٱ2wh,'T:A#NXR (`UًW >By҂F Ǔ V ?0u% b9\KYɫmT$WuQX)c3pB{/. ImG2_Ւ婆 FI]O ~7B"|Ry?ք4M6*0 cbM/(`xO4*oGVbZI9E1JsS`"h!|cd Hipa 4S AWjkHuAx~f#[䂨Vu+Q}e&@j62W Vx8HCQ+ЪetigvZK#e³;ȢMFyv۝, JJlkws9ʽ%O*^0 =貤u  (R%e=[Ӝ5 *FF]=ţGɩ/DoIa#G&e34};7J(hv%n94APԇ$̘-lFkg 0И |v ^'u2KSߠfCMzy(Z *~P׌i@./PÃ!q+y,w?TAxs]ʶۣe&jTUsSr%bo9+D ("_Q֐1 gøI*Js nѼiCEM϶5Q 3&$-uji!rG?&ߘ8qW̱gSrLs^~xCOJP,8\BRV lkH[Z.ևQ$pVc-wbkyYRn B)]`z:4X*~',)WqҀ\^CEŗBiVVbCdM1G(Pk:"( jfL6HZ#QB䉪O^iU6{WY c k[aeFLIf㌺z*I+Q^]5c҆]3.SwsZb9J&]/?<%(X^nx`j{ R `3y͌(0OUP@L .5srQi&;BEM"g/4b1.ǟk%o b@<ާĸ͚9o $K\d_Ujm-lwӾcFXN[#lz+j']YveWA,/sTqC X-縄)$в{ Bv.hrns'.! 4n)\6t$lpMy۷TVu i!Nve1RHc@9@Bˉ( s&\jK梨 wғ%6Zeb\MJY.2x*d|<3z;FIGvSإ*^,OKf#,D'tB6( g\BhYR!;Cl H497GmlW26{1wSo57I >mnQUai g —حKYrHE#so|䮱t.h!)+6t^Z^m+d1k-ŧĩR! ₠EUYR+淼Mx59|<*%v u+Kn d}S7gw* z0jAgLvmWD&^hERǗ&v`7ևF]͛LDOy) 0w{݇FG;I(O/Nf$mэ?Tq:mN>t;MF>BpaN 0<{ֶx y,'TJ3<7pӢls4I%53>r_ y|;=g5z>rWJ:eMW2L:notӃHږ*S7ܱ_huˈ3'tiUD8W g5L_~ʞ5`_-+a >Θ56ڮ_LKbK)jqA?VB"b GDox:XaⲈ|$@"ftK~ j]+d}im#:a٫,~; tuoZ=į&"86LN>\],V$;n4T@Hp.zh|n){>o]-)K}́T"xf,9*&t?@AÈ: #;[eM@%Y%s\o_U, ~3DU:@?5 !Q c+!|1œL}D$pl ߜ|6ʋdtmCtqXvaywQ_ɻK~R[g3rAfgjRop&.g~Jn >JWEkvX焝 ,|p; Y(vo>PU1v $34xlP6Ij0%g]e\= Ӊw4@kke} u4j7,ͼBtdfe/S-'FV5LZx -_(ֵ@ݩ,(+X;40m%/J`‹*.GcLU\σ6Trw3Q{͗>G"nf3] u ̷]|k ;( ;Xv<j~%òw ;,B~Is>8[z<=']xt\Y̡7^&Zؠgl[`JκsA^;._:vAao,YߤjrT } XYGPVvh-RXQY|D:2M f @[_|ʸp>C+F.iR5DGi9F*爜z-\sLZSe_s A֯C뾭:&xzN.ōK+&qS[nWUL]샹3 i6OdsƏ斞ukT\i}][l?fIsP3o(!mq|6yLJ=!34YEwD<`Rߤ nkx),vR5)ah8JGCK-S*5ӛN%j :ۘ0Z Po竀y>e\8#p|ՀZxY@46ip6(iű5flK.Av Oe+yL\Iٖ#VAlᚧ߃?c (pD& %yz_{H5O9/h4dx 61#' Co"QNE$9FEDcs '͒SOX>碏@rm9}f5ڬ[~İ7]bQwgѕ}K:/߈!{tdt^!:23}B6(7R'n Aʸz@eyhW>ֹ;㎽6/ϨHp@-8*^Qk= ?4b2`vô&m*FAnL- }ؼyふܷK_7킪bX U`nEVEG|N؊3Fh#OZ UAlPn$N*8 SZ,Tje`"J0> ^$GEy`kt o2Jp'bdD?$a_v !wH]@5m~ /?9jr^QI 񊋸-CBs) 7/[QW·v;PN|&1KnĢUaAG)r̫j=w ELTܘZ U1,˖07 +W>'clEz:J,{"oV#|1c.EdI?h"לJۛԉt!u)a`č& _Kqù24N%qy/9J'W|9V\3]җİ (,4v߹l^]}UPt4rm2I!oMtV)%2Ѯ gޮnX.c{MQZq7;R[9PG$!/Tj kWM%:o$ȊqK+pi3}gۨye5 *8j)1=k>GVT$^C3Hq*D} ϋvLCb2ɠh/ڃdc~H`? y8t)K(:C{9PXhnoW7, NЮkܰPô8/hlҧCؠAH{(2W][enF;Aە%>e*fⳫT]ҏ?[BH!be 9I֨zkdE8oY*X%Gr1it8\dSAJСU^=߉\T2N ܎wJh$<uh[ {D5quhY+X9 ofʖ5,Rx&^=n2c.9/If_o[ ŠV/p7`QцULB^$TCgucz}хz.T `$svAT]7j>D1'r[k2үp צC,O5۔Hx?Л?+.] ax PcF%4Tv4V̳zpP68_:܃Һߪ/̓WjX1K\]v$efΨp\ tmr~ aE+8i0ɐnчz\qw ;0G׬^gnS3Wւ@=k|*OF0\;񡫉 +-jRE^7yE<&?kQ+f=8(uC)Kq۪1!- C;)#9J1̂n|bNdUgS@:M>"mK˫d]0H7NF =. *Mȅsn(u4Qxҷe.~WMpt{gO}4/u-%$!^[԰b?21sRC~ZoDzi6K}C4aBI7ya o3\HI># .l~_!fK+O|ٌUEqmte%f`i%hV/ᎥDPK ,~Ylj L !zdƵa\r^̾M>6fām2=[3b"5% ވ5AXx-Iέh^h,_{q܄Wp0z@N?I3dĪƠWo8q)Q,OR;PF ITlJx+wYx![_  珀Q*6%<jQҭ|R1=,EVWDL|Cߜw%ݟ35!&k*Ĝ oȪML{3TeKSMRNg^Qǣ6"#|mBa$Ek׻. MPɺ7㩲T%Iz ͣ)aH I(G#wolc1'AV9 4xL͓s#~֭Z z} 'Ţgo2bU_zc_MOA)+7dܠ|e;3>bF˩Ù` Yt-eKu+Zʖ"&[>^Q{XFDFxM*j\:QnŨJZI8(q k9MlD1F: dl(J;&skN$чܣ:].*R'VnGȫ{gkAwvf ũE@=nK%0LS>N˭YBSiV(Nm# p*;ޯ60'&Tg0!t I˂"G@oiکt*5"92 `'!z3)4o@z:QsfLꎹraWl } w"E26X%'aϪ 4HWH@tq5Tvb L:ݙ@Si5)/rh' Y8Foîp 7RGXay&Zfe'UDm)'w/s3gTW8.J:bl`N#Lk΁:}JyfjB?X&Y<.`B-Ň{ْH0E=]r =؈bt3w}HP%\쎐IX"Z&t|Dc@|6R/]E5; ?[2ZWnP>ijm4U:dzEfs|8yޠik>)ghv1G0祙ߌyD 9A˵~$9oɖ;'S68>ua*Wcb,vXC}w) Ylp|ie,9T.} vGHxs,-_1L2wy!]Q{iCpqjv~g2=VB@8"[|lE?tօKoPպBҞ4b E-3dB LџKʰ1;[w^'9+ ݀>Pl(h„n#8b,G /Q J _ฅ} Kmkhٱ;y_tȁ)گ<^]AB&uG9~ P6Mq= R!uxVsni]cU {\nAX6}Fr"(?t65y܂2l <ӕN$rVyÙ|ϛ `+g7PG.F}dc` EO$J`G'm[W|U7!S*eO2C4 PTa/SP,h#W"sOA26ĜL՛]˻ h3(H߱U;$<߭2D◑0 f;x:kAZ_/ @OCV(>dlKyMiEW*? {ƕY |S!dVmW6sc)Bn85'xH=i ^ HoKfuY،HfH况IFăB [ u[]]5HpXIIPˀ z!ӘDfv!v?(x/VLj;fK"V". qGGU0:ob. +^qzzOfGо[fUMMG>ڮlSsW̐ ϩK$a8,K(@pd3JlFe$3$CCNb ʼn# b !U~ܲOOh>,`[t'ޱ\Bk6eB~PqסQL,/Dl¼0=q /l/n=oGq*aoaRs0(s{yvh_j3hǁ*ݯT(\ H5 V_Edg g?:L v<"'1僥P1Pɳ׳׀Dg" JΩq;cY8$7;72£烪E&x; {A,Ä!@@qUw<TwT$Vһ22tያLs1aŮ815dҝX# 3o"kKKQ fDyֽ1\ŤyڷŹ_^Mv̖DD\ u(SE_(_i赶Q Px / {{Sj GuaUAҁ NN >#QxD>*0/#g 0iiR1HS_gddeyURRkbg3|BAAM\ת_OE\E ?۩RZltNRDu&WWуhrQ..c]'?:]kH8}@1DYnIpDpEg#e:[y!slZ8}n $6ɩ+<$/$;a4Mf5E4RO}enmN[n[ ԕS51^1)ƛ7BB1)-W6:'e)e"PPd; DrK??oYp/bjg/[FՕ9b&5՟eI"7HF8"8|`@c!!ZFt(̴PGqqRҦXe##c0"f݂˦s<7]pő< y3Rd~ ҂>! s(8'̋y8yS%99#{R#jdgw+iU:k:4G : ŗl}Oow-O-#L;5~Aрe*Ft,,rn{A"K~bSZꐥ _,Jo _ 4n=b>d=$.irLjiG_WϬ8C^ANaýʣTXXciSF, SD}EyU)3}39rF FUfXEgCBc6ƽZ[𓣆&W?痢 2^Dq[MRff>r@X#:97hz?|1ȕz-uHJ!h+ߙ]B&Pn`& dwX *sm ;ɁX^:PpN[ձpJruBi`h u o(2x"92o\Q44m?{CnYƘ4SluHWXt݌xOKJ6rG_yꅐyC>0B V}-wAr$\% Az s㟣uu#Q b;L LtqaYimz/Y"{|,زti''?iA|N@災J/3-qaT-}9.vQQ=[(<Ig6۰$,:FF<`+}QⓎժt h} .tIb 7YHJDx%}s712d6*4}+*+N[>MHlYMYQVZZMF ]1vj&&:ѫa>ȡ2+a3*QCUROnQbkPmϾP;̨ϗR{UU!K鐬UsʯlUlvб<(\\u+Z~\78h }~}xnIxژ =ºCǧB:*,شp^fZ(¨ ZY)0SkbEj0jNzMuT¥25g@yjȑC~t@cPh\00;"|~~[]=ͪBZw$fTE)ˢĕV-?.4?ai ^@!04Zw,+zG,[MW{wZ2Ni{f,Nz@Ր)ޖmtTX?sm%7C~ʜtt `ĿM`Ԝhy-oojQ Wq`^(4.Ww$+l%-[&p=ᒞ`3AR~ڝ-'նܶ3xr8]IOr04N\ 9LNz]&I djA *,0h/ pڿ%o-Kkk㛪WتIc5xYP %5V )?9*16pO#ѧ,Yԥ."6a^67ǰ.UΆ.geߩC+`P-Qw=mmÈ;>' l%E9ϞƥC9C|v#xxh :Cƻи``J4CZmC5 %X V4X϶LM1_55'TXuc;"@" ڻT$ك<"6W< @`r'Ì:겻&RAg&.Ja8*򫁆*G.9d_G@NA]֏e'/ 'WSvܷxt* f8m+4~ ؍3D)k*Շ ՝W􏠳%iW4.Q;jQx˝ Dl xL JҦ0J0gm(89m0q+A6)IHyqtyeL2L\yk p T'TkuY)p%jrX_yCjH/ U Ͻ?e.>I Vh,>ݛ3%ٯN7^N? O숙U$`zPKոCs-B<=<-. C+݃"JҥϮ}B[v}&Nq%iS%N0Ϟ,5md#Q{V˹d59,Za5{O~JdMjxZZE`o"۹Pp=r`V݂lR͒^=ȡvɠ q!mb|~ f͡)̮z@H)Y^]>qw]~\A|X|;]S+>Ly{ye+~n`mYHAke-!H{~I8U ؅ g-`'W\ّrM 'C(<$aĺ(܇?z^%X T^2@5J F1Ͻ #p14sy4@#f5URɳR[nc=/,*M/&P5Bۂ'LsjCu;&(]J6@[mh6t|Sa4S"b'!X[˚sO+Yˣ:h/_\'1vc:<7"Kz!K3!6ra-ϋ=!hEdOxYOS"/ttuSFDO2fZ)D8)Ni $4 8y&+uo+>wvDGDwdz"3GnTz@ iԕ# Oş AkUJKniu?K)-%l|न6p/0F&栎) 2ݼ ŝo$? ?yؤY"'rNKe*­rϠfGꖭVG3J)pY~)G[ F.AaI54L!/DCsCT} E 5DM 7ܪޓdgG`lgb>vMf)OfZoTh~]!μdHݽ7]Ek#2TE/?}8澖/ %I0]. 9P]'at_a9mި SBam_KX t_Ms DN,jh}:嘱}B,]̟#hUޞ{L {ɤnUo2Y061n;3 `ƣ3 gw"بx_' -*F4?d񉕨g^2K~$qp^*G 78?#8)* QڲZBW[la9jS9P[~.EyHI9@! }\:!Iߣnr$JnUj1 om(o,ғ?70$$?$vMޮ^v`&i|QfBlZc]zsB:L;;T@np~F6>pRTD|;H=lB'|?tQCvg3E vA9[hQ[lH{`lT\<`H6 V5{(̭ArBtBǒ+­%\8?f®(›Ԧ/r">$\6e!}e>9R#d3oyI޿H0~>@I~ɿT4U#+#pjnXF{gy5hvzWmN7W C*ib S_Cs_K(q8ŕvdef+Uwdܟf{1Χ{|4CR6zS웑*Q8Evus3OCڏz٣kznו^'R[15{􀵐59RvALYPllAxpЗH|:䃷) GTG䍔+<;-X}bάb}p 7/ f i <8h$F>M[VF Bikc79%7˰aV}&xvw5xcWKcWNcE۝Z=_xW0cv-KI/ЗO׫d ߔdOc͜ OyfWJqn= $%zGqt~q+%5ipּilJITס1>⚜9*/VE󧃭sEslPZ~r"PlP![pI,eIct ŭRW& ;{CwoW/;L\ѧjç=LZQu, ~KTM|rW5ؤ|2&LJ$W3-`wh|b鼚U`krG7IN rub\}$h~*8wgZ{A)!][\_2]Im[ifDZgRުR:}ǜsX7/4ԤF+X$`#]U/銇} $ߋgA3io~?p*[>i9z/N|tb zg@;2%gDŃƧC{04|>1^Mˬ0j* P1Y6DjPERs߽]Ccf3K"-җ)Co-x[یTBI t-ٳ+)P0pmn7<$]"ijHIT,%"pFGU Pr&=N'>:]1g=4dE, 34ǐC"mY 74iKt:,ןMn^/hkV _F/ǤVjK͇iKI<̌F*!k:_]S({8צ_% e^|,ޚFY$z Yd:L&D{Zkdx6PӒI|% X6/#u“(J96'"1Q xC{Q RxrcB]qx햋t7Xm5_?vx Ȕ-f_DWX~ck1NTģjF})5o%t|m)ʭz肥WSҭ(À|)Azcs|x<\J($K@>lG=1XQ𤎫%;1V+C27ßߎysc %nx=(H }J{ 5)zPِZE #h O)1 ²#kM$X ħkѩM"y;Vo2m˩k1&fAf=tAi~?q^W  E3}+Oyqb5Q#ߕsJvb됟̅D~;PYw`&pF;nfF#LWuOz`!p}qu@ꞣ+iB}Sjװb0RК*ps }XP*VCMD vdU]Z-eP6)?FR bUGF+v0 2pNBܯ'%*[Ɉ'v "cL vܤlU3wʳ:a64*kY87"a6pm7p'T0Ѡ.TA'w:oHIpro9L}'VOY 93 l~:3HXz=0KH8 ˄mn$  Q:<߲Hm;icjs;ydLq @ 6_ChqS E?_\4Ň5A( W"58@׊fMV8Rjo6JRWea{ S e -㐒qWGqg ``07- a\a&}߻+C.=ɬVg:d n.E-N(StSr6z\AF?k$c,=Ī$0A鏐~kBKIBy98IDK72c;XJD6#t0b[XQֲYpugnD<*lP(h08#r7:ލ_{ӎs:s&Cy靱,ɅqxRGlP[1lj"g;؃ =30.ؼ@8PɄaՋId a< 9cЬp!ى<ቬYRL݃^wh4+[*CQ\zQtU \~E: S1Z̾,)iա!%'ϵ>!Ufwz{ű.ndv瓰wsuLo܈GxT xP5HeYleq]Jl^{T coiu(BN<)Z\6~(\щO ћy2ChV8T>d9j> K-Gre6/5U/1}_΂U54ݭC}ftٍ.e#t4A><.%tAP\BSW|^7P!VbLa _!Ub)J EWNUa'@>V^ͻ=~hI͗ U2N | A|Wr9Q=.#|7MT7[Gd2^呩Þ.RU 'a-.L?Zd ޞ@Yq \/}Fh~(4mX9~^ S$=j^bi_Ǫ*jhy̵`ǂ+ׄ r[p+/V'j5f&wVD+b1&MџDvMZlR~>ng8kc'!]nVR)&`/I4̭tgL {_౹-2oO {sW zf`Y\sQ/Og) pQ˕. Lf +tof֓H$Ű@8K9sea[ryX,欉v[ۭDڲ@nhGc2m`[ ckv[٘!ӽ;l6F`$*īwW۹bwwVgO`dAH?zԗE!qv8Cс9{#y^?Ym_@&~07<&cT ,blԉ!"9N̦%`!nZ@ŸX& 8 M,(+9,YsVMu;Nj;̍LEl] N :Db;Ouq/O({'E ]2{c OnywɖCOZ*n9jS/Q>oMS#kqB:>evmlHTK1%ZA'lEDxlEWtG/(=lh$QTW0Ueh#sjhS5X1f)hML`nu$xL -镟Iv3 JpA/\J=uwЎgd7ݏkڅK@-!deE}_SyoQi?sb8L̂M3#뙠j&Mv;<dJ3 Zuao%Zq?H  t,x?}ӈ 4_5yUˏ3ڻ=܃ëBD"ro /YONI=/+.̽X?g,}- L;%G ^g| `d-sr(;2+m sR5Ip%̬'I}PfD7~1P-7]e3v$_}HnxE2 L"FasU:sR!wzYuJ$=\bD } g` sJoc) Ir6-ǘsĮ]wN&|z`P/>\ 0o2ذ깴SiGH,I4c5wկ2[X Rz 8bZfV(_3Y:pH\}i[9 yts,<yLR]WgǗ3 mN1I!TF= djmքRqB obWNAzGp'dCLtOQ#L'f,:AwOd$nq{zDޱ3KHk6esaCympI,*[_]$F_cÚ P&vdV=ګ!* .LȬzw&7rM[84+S@q'>!?Z9⒧]d Fh*?oa@̑]%&ЩV_E{&cv#oôR{Bf֜z^V e\?[MӜWC{cUl1'=8myĈ_ !/XU1 Op)ZM~>=0(aiQۃ 5fUg!Avע8uj̪B h JWa)<|ʿ!KiT~e, ΏOv?`#(9N%|Bխ~вO FԾ9b nVsLjNWA"KӾ0m74B_^e;*ɫbR'r |RSf ϢhC&ex$KoƼ4 4K<+a5 +8(WtRx*Ya6WBS&2S0cXG}L䂭f zdAJ t~zO-: 2{Ep3;r/P Qymoun%,!phVMS hY}#j_iJB\=$69;Q7yO=H"uo_ $2 GhT-)3Qsu4<iBc^Ab9o Ǚ̩qȂ[GvǦ"#YTl36%H:?=E XkTM6,Ldg`uƓ;W0y|~g YFpÅFP8rJi9:DkG`\N.:*"j4{w@a hx5MV}·,~Z8y-c~<r# 7|j>ĹE4Iy6ױ/Fe"8Ol YDdE+0j"I ME3Ӕ3cχ~HKd<qW&w)Ͽ|0hCQbSYHemC_wY.֨UlXڝ_b BqzfCa]AxA¼0.t|'wT0tTD1h|Kkh8$.ߛE?9j G>5[t& H܋wQ6"2Ci5 ţsyÛJj쯈lݵO\`E`計 bk!/ 1q`І?$Ħ2Q ʏڷoeo-7iۇ椡BA꟔eHz`8%5p=tN݈x:Fk@-n0ԧf27Rm(uɴ)ځ=ͼW%>V.9W+MߩC&Df>~dШZxgڋT}L5| >Z?nu#8!qlP-eu [j;HQ4*NiR5vD6ƥ.0æK0B{UIk\O;t րZ)`}!3k{D?-c/)p4 1ȱZrs$St]kۿ-d8 a ]!ܬŘԫ.9:V`gD$a'Պ)feKJ6[N]'?3;d_4.2ߴk&rA3=2PS^и @Ⱦ.1,Md@x{Э@Go[6®w>^H`6F/Q15K u*+@Moߤ#⩄(c7k>g1r CU^Rx "rplu.?mi]{өzU;edv(N~fv/h\u dWV/$0#3otmc )$SkwU}FQ<ϘW#UЀMLjNʽwܲP˿Pƍn$ֽ|bU僰JR|D%+\~"һ?\v_2b;y N]$7< N&25ׂkBG)Pxٻ!{^ۖZwk"BTprK?(}npFİmSj%#/ b #Mf-)9(CuZȯp@£$ź2|&l: ^bjU TGV+ʐ TNÁ5Ѭ ,l/L HgOh@KA!(**'V6Y.JQ;Ω^1HIxUz6L^Ukuznyechik-0.8.2/src/sse2/enc_table.bin000064400000000000000000002000001046102023000160600ustar 00000000000000 zɮ{2 ТPwNljbA^cBi<ӓO8"eF/׭689joJ BΪju8ץ;j[Ӎ { s+@U3_(ņVfZb81 /!kb `pFY g~|yvA!gUf5 QXlUְ.Yw&N <7$SbQCh0| +%Wۼ=)؅Z1 4>_CmeHY+W9.V>0W;u`gdLTw'|t.0Ab|$nPì4EԹ>ia(0=>~Ma+fD&CMJs `%D|R؂JU$@pՕZu;b2_v\Z)pO 1UX/S+48[Mr7c_F$]2ۧ.4_d;3?'lfG# دb /3EM хIi_j#}Yh Ss-dޅ` g } Db\FJE-K/ ܳ{4j"*:̓:easty 9[Œis98*}E!O@^9F O&wzL. *![n/X&=VGT UÖjb݊ўyӝI`ߣ?vOș͘-6rI )coPQMv)4Ի4usQ3)>9Y6V'* -},g?"j0+EdDY l5ךXb#{87\}\Hp:֡Y+ 6Ÿ,P?"~\+ue-Plq!9ԮZ=$JuYzw!Rsp Dʌ^ml r1g>k7c(:UsaFwH Ȟ&t[k3!}`)Z֌ t;O\쉇C.y!%r;3pEݛc-?D{Nw]sxKv:, {wבeHK3Dz{qڤn$dOwA:̍iXɎ2o0 \շI,HGiS#0$/VW{ItX+mAdz fL}ˑ'q@Dg`Vϊ:Ȉ؇9-y8Z~,/F6`H^>w6撚3l²TRiN*4QHGXc0Sґ2&pr"ޔ xR^UC{]im?.ul.N\`ŻG\?3"YIǡힲIq^|]jW-G0.ƹ6Dv5*>:^aSBW6aY*xR,f'] a 8d-h*"G j9?K@hOgH LGl1뎀c/P`z|&zHe굲AB愩#oMDŽ_[*,IpmHfkq%a4nZOY/>ϡt5 ,e~TЌi)Pr,RZ_Fao?*hF dյ],6M˾TkX *W[mҜ<-ThG 2Sy;YO†.Ԙˬ!z댨qB b_څ=FT!I1Nίmhԋ1DTt05iC2"6Q(聝;PpVjef)'aɒ$xAݖ]aCfs<(!AL#QOh fSR| rō7CEu,F̿]N[6JK4qt7h_@z5%8`MVhp!,%n">V@)lA%IX˖MеU#W[?\e V^za֝WJ hܴY#"vG[7Q;96$=\BJk]tSӧ$sr |5̈L}$v3Զe]7-1^Έ;QJ.欫N0 ėQTvʼSqc"[;'Rb*;e^X́.+/=IN! _yRϠ X[2kwZ^{:-Oz~t J޿)w&WqTgxZЎdݚNt=ߺp^XajE :A~@+*u K51Q%[X[7纜*ltLCdx@dM,1.yCase6kC(9)-fM#&%]̦@Kȵ!|<)}(?KrpTB"Eߓ.$8hkK|C+}&a8*~h$}\bu'p7mŀpC2y W^q?sǮ}:RĪM&j#3M!)A{Zgw$=,c旡M,:tK 3Jd<;vΘ 3vIԟ- n]!'ck횿8gn@_mķY?^ynm˸5mX7t苖@Sf'ikZ@]$<7nP7^|WSB\PGWqÅ- .N;Q9*TJN^V>@'aiu3!I^*ܣ#SMo>Q/Suǎln;Fp&`wYP; EZcsyџfotdz+ f@Zn!`ZOՀCwynOP8@ci;mHmp0P)+uyZ?"fYH뽘y_}Eed!DJX;c27KD5"W (A5R Që{Cx iÝQoyS2 0҉v+Pz)5 {J}Lx$un`LKTHlmvPlGRxqɜ,?)6Pe-Z]jV/F_>mz-xҶwEWRځJ6ojRJÏ[6g< or"DBoC)-"K3fcCأ.03= ʃg܀wuQ1"'Ϙ6X+F4/7X%F.ra8p%[7TM7k X$MkBy@9KBvΏDM&1b+d`OgѱVw)IGɀBC< YǖeH5PxhF}/\ qY9i>[*p?~oiEݶlxDEKBk~A0&rP UGFqZ%oauZ+(,M߷ 8&2|؃5W 8HK0=zX~9Xm4(~IKiԅK"+K3~5+І=Gl>@͂K$(J\OI( >j9d>Eˆ0z bt|N/z*m'F?,eʪ"mTN1kc~XWDڢbD: N ]lc [`ģmԦ2cr NI7GrܺBN֓pL8bs? c{Íө2] >~RlZM;uMVsY,9̋$ }/U]#`$~iȿʳAt=fY ճz$Y5Kxv2eʇq}?x,t ע H?+`[vꚉ,Xyr[*46a:ʇAĈt%.<>Ak`U՚ۿb|hw`0ˋM^1>|40# 1)O h#s\Y`fz Ws7r#80h(Pӛ![vk\a -70`UR.é^:w'Aļ!Ez?HA#{9C8vqI89yBI/+p[r фԈlb, T?L.-a' =3UT.ѩ0TczA)|(ִ Lgt]2_\[]xq]BcrxFGUb RF TxGಧo0WTQ$hl=(/GtW-@Kk7da dIէa5 +BR#Ν -t IBTь[x--3ͻW$JLݺ H(=xn% =J\/ApJ:k!*F<6erG-KwbWw72C+VMh)f1X5ShjD6Y1P *jUdެv DMC<2A-rw"T&i2m2oV;y"ME.n d:gV=.4\_?E |G0]SNJBiRG{ Oo2-_{,Z1S :E~_f- xv?_vdn.^lJ]qelN=nȒ'%!i)js#heUSWz̭w3UlԤc&<70~&L .Ϥδ.[O|1/U5ȴ/<:Q  $ Vkh/^n`HzP^G艮yO+}3E БR0bjS}` gqLdy;sjɸ4IA | N2U 43 ,EdȎ4z ^ G]6{CƃD &ћXv Lc" v]߂sREP~D̲xdkZ{D&L:d(O/q>!B.(9 YOV'ŵ}L9<Ϩ!pc9 :Bqk֑p"%,TIH"?de:33᷏Ag(~6\Z)<٣N}Fjyhj*_i,q#Rj{,4t#e.t=g1]:>'N=bvJsߋ#Q5b7iqaiT֯7RJ;{ZR}EcNWcK8 4$ iN)87 MD5 E }Tr/>r<(ضGfϷ3` 3dz;zkٌzCOA.F [ź({5V 6TRp>6F~pn@TuCjsL'v E3gvCQt?Y@o6KYQSZPV;d/ '|l%-)ߨB5P& .]0I% $ =nqJWfyET2#kE;/ㄴWowC{;?$$07?*C Ú/8}پ?/v^˫Mv$K?uinK_t XK_֗xč_=Z!߹L45Ia/M*,/=cnPKuHxW3PjCy_Xrj% ֊O #i @qyHI)6A+q8iW^Ddϕ?4F*֊-M7(u䝂 U /HQ_Q7y\L΂2Leyt&F0/p6q>]^Y]gD1yV\tƇkbN^utW7NmWzvZxz3KAwx ZgXP)bg6(9?-(|6O Ĝͳc`r=pPڹ"o`E,o(mo 1j>+ou+&yϥ'r`@ Q|i&`є`-gvc0WIe,Ip[!POd߻ dO[RRU5f񎬉|VQ,VL]=kEE_j 3suvE8ݨcfY&DsEsfO)fMv>Y+ҷOs[ZZxjM ݟ؈ U8I k ኬ^kK8Tr'"YyOYxOdEhVO}[|VMqO*ō%M6U1Vg1,UC? CU&ܠU1M ! &WR CsCD/4~݅ #,' Mr\W'.&gAR&:D)Zf2ODF&l5O.&5aZ,95bgl`Ggd܋rj+jbNX t!XG>,hV I>.e?д/IڰK-|'Bl=H;|D\w=԰Fr=tm\~{H!-ekAHYm`n~nj+Sɗ{ L{UhBtBrACK鯸Pz`L=jѱ\L 2 ) v=^YȻ-(+2Y}>Q:mQj;Q.4LA`!^A(lyf?8\%+(a$<(C8f *#q)BDnx1AH&Oog4}<W6"<@-\`-oF$h8~0H.jX#9]:S`.cH`<\Tl`{HΰwR: ,k=D*7h¦w=22FR+2bdv8q(kT'T p k:<ny;Ȼ_#8N t#̡]jPqgz];|XЬ|A\~]wWcx5:U:eFK6hm[@6J](&;wDGˏDvw:cv1'\_/h:K_sCߍ_S bi;;L:f ~ƕ2J2ﴊƪ}v[B}$g0[.*60mi$߶;Qj!ߛdV{Q#d#@4ys41p#s2 ;fsiכ֖pi5oI׿Fk]7HdBsz]cZlBfHߪyE.~zEsԍaSutաt/"SGTʖG )䉮ϥJ"+C .$ŢTٻ" u-3u "#R95{51[R'#憑_^0lP"r~ZPl`.bL[.H"S4aS|t>1TIJl,B}:}hj,)H@z┮)^pq$~K2^gWi%WK!zK"7e}!e6\$>"~4+mI!:\} !@9I|bm!q 1F > `xAcmk-c1BB7ƀ))Ր@zN|7̤y'7ӊ : G4:k͍-Taq-3/dR3y ?W&;ҾPϊmPk9%Sy8y̡'8(ᢲؐ,|{-}V B0>шWW&u#uus 00\hPc@vEډz2Zr>ffRݪM[qZ!!c@`|DRNtlZRےGijLri.[-%lք+$4 _ oJ,_ cLc7OLm44Q\o}%xD$=d>b=nS4ƘYnC{?8"8FH7"S ,FSi7%ms?XTtmj#R{#9? E\EO_ב\ML2)݄K! b # bNCF[/gȫjb/pl>G>B@G#6H%28h79ޔ:#XӾ@ NN үA>hOOц~U.ƄU.ǩ ̐WsS(tF|О%{a/=I%P`+Cegp /h0ʖJHY7ˤTمuMz4s cOtRqqGP̼ewwdч;mm=wQ-$wZ_u{ P"e)e':6W-)Jؽ@N|Ai?W&SS_|BUGGq TwT<wUqޫ@@!҄û,A58nB)csW99ԘK%\z7!ШSia?4.?-VюQ}+&vg]ڹ)M[,Gz+,wTՒ<(厠ɫ[K!UU{Rχ< [n[Nmneo u h`iBu9. ~}~} h87\~ZYo??>AOTaqtL L;b Qiq#$Ip)YZ (Zf^Nj0jEǾbkS00c##eX`NA^C8W_'/V}93}3)UyE}D[֯Z6C$3$eFlېJ3dp:&&jv1]cXXTʽa0rOI]8rEԷȾ}ܐORy@g52a+2>aͫ BăFIĜs* X6IwIYK::Ne?X,f{iN2Zw{W/0*>DxQ#>..QrhO~T&uɮGm8e 4eK217sCy谮y_=F'&Q.' 5CmZC4J*h3j̚_hvBͪ=]͓[4E5fM4a;"^ZSb~K"A{Gr6JK^XɁ; m߁|C9Cũ9E^`T | F$|ǻxInxy{s(0s6WmVd!S| Y2Ύ7;7$8Ycܳ;vlUlʴsUt22Vș$3y==/GvS{{ /Hk]:?']c!\kXōaFkAF Fr;66eZJWoVHHu-z1|?zݱ^^z`G؈inJ**~֛$/$!qb=QQv.9}.bo:̑0rJp[NpP:B)IŒւeddg_SH1ö6>Mޟ̒^r4)3z!'` 2{@dM=-'efZ&yaĿߠ0G1v&^CX]pҦ[? ;5EKt?{m _yHAzd:ݯU4%Y`1F |yVr$N>[&"Z+òex 7أd{ cpqh"J2mfBd3-E b]=E0HuKe-tY 3\:xLٰ~?HkӺi<뮉&qDJQF.GP7g+xu!R =q[bWrF"%RwvbM̴ hXeN\{ Uc]insV{-B`-,sşxHGv }.o녠:s SI$8|ܺsfE`ýFb>ViSBY6PpzV4BjfyJ}:C ȸp;*p #mN(KL ∔VA'1clow۸ƺ҃:_8p0UM#1?xHД5O,w3tbɚ= ru/4}Og{tpCm`v4h ˍ]LHnT r`x,a4"㚕 lWar˹L^hI8@I U4%GUdֶNb|nM6P ~9Gu&/wހ')mDU N2T\rF}6XAnH\3o dy~ģk: BHJZGS+4F0վt-+v̑3;e|ܮd^gýNR߈M!53ݯ%wCIhRfBN5d_"֓-?F)ue9uOI^xy S';o9$~'%X621Jʳ9#);ayΆZ9s!aD|t&/)5iS@奴 %5"brɳY}Nks&;J(ld ӗSޝKeT3{LML62AOs`>n]؅3Ohg)>kiɠy q(8IZl. #>Iu<$hJwƠXGR7 pޮõ 0D2Cu(8=f+QA9 Dy;DlTWΉ#:񩁆3[=1XjW/Rg'%jsSMp=~&$X7*Q9 0q<ܻ׋kݴE$aB7IBa4C}K6iz<} `)bٜLvPeDoZ~CRs18Ƿ@BV+RSvq6ɲ0i8+Ea ~rm$OE `cd}PͻH}ϣ0]dëK̋ @ZBסbu3Ri5o +9'^A;JujDϋ.ğq\zn=mhd8TBnz0pW!gf/mz͌c.-G2k[r'1D>j7]TA:ܨ$Y}{?9A-v ب_Ft* Pp/!Y&CФ+mC),jȑe$vԈ]\K#G(I Ha)ƲjlY~, KPD(VCO@ /}Țz Z>_?De*Yq](c[VRX SN>S̴L0%KBA]^<ͯvs$` T.z;\0FO*|k=m|#"6ǖQ s3-?rmji>PnWZ2 8ћ.ke ^(αkL#N`l&1; *锓I ܭt!0L($߆ AKu)Қ58?|Z"XI\%D9#,7JngZVѕ!Oi!j](֏؃<$;UgT&'06xJlTI 㟸©O{B#xa ].+?͊-xXA5:F1DlM9k=2g~˽vjU|W[m'G`JYƣ\/86~6 qcujXC -!1qK)}zݼcugCo *kܷ&Ǥ-NJ榚z<3EU|#nh(l `wc]S={|LDWʹVE,"W#h,PS/aT7^ERjҦ-+ JŧnQ:\.ѻc2n=^&xR%T7PM .. q{_,h]!yw2ÏL1_El|["vT4%FڜcP NvQrHUT9,ei|plP 4C2Oe*S!7FP;ROn=@EŬ&Nb`EUwA2.:[l< [hJ Q/ G,b8E"w }Ӛ2?b[^!$%-N~#sLx4 9DFX{Q^¨b:J.8WTg3sH4 a@m1fZ~䱝4,'Ӆ< l2ܥQ4u(nsM*_ZAk:x;f@̱W3S Uw5Ϥk fvwAkg{k?ب&]/R6|@c>ZKOɟjpa}O\@Pm">M:@Sgs\V$ѯ~cYt ߔ\pf4BԺՅPoċd3·I?N@,ɳ[,5ʨfo )t_y;hkm .= FN7Hcz_Ub2og' yoP ZEn`U Xb_20=7g ιBC- qFcbl[h6KJAGX@VNQh'y󵗟{ALU}ݓ]^lvڛƶUV*NcJ⁖asLKAqUWIK?rVmZ3כa9TӸ- &DV.=\}D*qH3CՆ|p#8\e>!a\EX`G䯩 î70,1Uf9+sO-;XmD~ȡ\u"p.z:sI~BAaUĺnKη̳ u ]3fjYe?5Clą31/nR@5j\tx]ħ'=<zFOqm(֍; k|]zElc'>W++K.Nzx&Ѝ죬M6HC-.|`Dw#&bg6r1 5eygԋ}U ZO#hFV#I\Ly+ߐ"Z\$CM2:D|YQFˁK~-4#7 )snΨJ~ҥgD.26MȞsIf?l˃[ūݘ&ULR%B4ku)_~ :wg0$?S^jM/IA n'R7(6K}gwQb]74,( ėbCLv $ȔxNa%ejT,ZS 8*XR>+},]}iƎ\Tk&-Bb< x0M)a9[! v_a$?Klf5i(6H9G'"ISaԨX5 *@pTB}32:!^t|zb2R =pTk svn Q,LItS{ͨw 6|qm!(o3PXvkEWJ> IQ^rj9?/]Tf*e>χ~ U?!I'E꫑hF<*4Bpi64@YxZZ&^7脻YfVSHo+19tY˼HߏϬ/6;usdO0Q#SlcWk ݰjTġ6i 3] m#q_ƻٌ)j➝Y O5Y,oaAv7 ӨT|].8t߱p|C>8z`.)t8y ?`H~`J/%m03N|GEVdMP!<{sEY43!=JLy,7j4u }ek' scDEF.&poRjv,)xkn CGJ8ha)5RPkN ,7Won(XnWh'_i7yر} ^BHӎ1$r!@(_- xZr@} B ]g%0jI6ҷPl } TrҎj{PW "a8fDp( ̙+E͖{FLٱĉlb ͵~z Ѣ ;x!T]1MhWhye@zN42Ќ/5BixM{֟c.Xng1&|NP;v"N2r^M18%J-LnAF*m&:%M3ip+Kqȋ$oHB:=ۂ2u"MtzudO^$TVGȅw ^]2Qd%۪A;FЌv`>'(RC;9$ENQ"oC '`a)u!tԛJ<vX; eO vAڥ.o|=6MaʀWmc[+'q%NbϴimB},1aydJoןv(ux=0Ww\ULcGޤ.*ԋdt{!߽/:gmiV R`<DwC(O:Ȟ/!$GP9[~[5f}9mrʥ(ӛZB/GS*C+{H~T&7#QpZL5VF'*8-@p w=jr)GDd会b'pJ2g[R~K_QqK_ &q6~}؂c?ߧlA`.Rí,[0rÕ ޠ@u4#,; wԲ%~j'J9/yq%sQu)?eļf{( 3#16 ̅xd4ėhɡ/r~^>k=1)j8*]3\V9|WY ;p|, 旒u%¿, qN$nPlALUWn[Sq˵&U]ڄ1Xb? L$`HWPey+ζWQ[/G$^ >0J"`"h?IdE.c*\dUl"+S.eSZLs\-z[?ڏ ZIkUF<-cd/h2-ż g#^rHD&ކNm0$*Oոi0: j%NLZ#?cxU7_K܅jw; mD NdXOS &Fq+#V燻? =kQ^;0Rp_~)rND#")Gz8Y{ <-@:CA s_5ڈcS׸"OD4;X+(͂,졩o tk`yE7gT% }?ioo8EdkzOlQ)I-skʞ@4݂w =\eo$\ܟ{_[@ f ̧x43$ v1U:_.;謁^OSIc&dŢ:zy% &x貿i:͝Y-S/efdtBw5qOTߜ*F9iGD5R=݇ |Yȁ;e#K~^sLrSgeϛ y<* (":kPY]'j+zϪ#|n<@b o%kǢ.1KO,^*9Յ5xMd\K$ o9͸xS7ſlw%S1d-i"Fz°#JxIΎz†NapB>J ,ɝʶvyFMg Ը(?/]&J9bZҟswpkC#{=aX ӺuVɒTb?z#C\)n4 !.Aqj)Kb-k1d+m^Z`~W.Y7yMpl$t6 !RS.3]c5&s (ƳB@Y! 7)͖cӟwl-mjU_L+TArXD3?S< 1ևԮQg0SO3c}|fILF’e*א.1lZ*IgC;!RYhB\צP~* ZBk)>K!&*9,fx"T)_8d ں׾j`xn^X(%K9¶{<0 뀨uh_܏7S>4-X CqTs/,cVp$Q0?ʭ͛ V"`SsJֿ1E9z.pHiQrs5. W),'~*X/ThP<0^*Ó>о{)n|h"&ϙH}#ᛢPA49n%vhut ;~,a:KмL_65l#[NXF̦\q5fgMj S4 aڽprU2m]I_(G1MֿdCbV1|!+ңyB> ͦA쉎Wevܖ;$V,]O%9swklJ1p{\ncr.'G&P vXm8_z(g2G< v|IZ֛bVڼNV2/-]ODL]FGo*4Ox`(/z3<|d*FS(?-[RZ )G b"BV?y3` R {)XQuW$%@Me[;# ǥ35%I4slp7ݳ[1Zں_k8ܼ,PJOCxXYu JouHkjWAHǻi^U'[=e%R(  u=롟 >2=d;l_L2e}Q+u W26jί@&㉶z5g=w[=MҼ Nr!iju-$&3 Q5on:L2WMeQdqaӃDTP~{6ȟ2Wlm^#Cx9`T\3OA^'yf}ÖUAD;k0;Ee[)v:JWr>ؔ *wg7S}d [p}QL~ WBd}Ew8+_̃}K)-]o{vH= u8X$j-m]Oԩ;|y _r>}1c=Fqr(džyzMC+QCH8xVCmtd6]sxAT?w,y,U_o\s%YY/fA@0'v%*<|_>PDYqt,<֥"tᔱ|osX$2>4G,hwƳ 0Y߮ I8찗ѯAjε0za[k c YW{=0 azMECiЂF{w0 )y(I;GL@PUO0()ia`PFM;t>N!xfNX:"3c Q! n sJzJP@0{Oe?&[ʗ_Q ANUO@73`qN-\9&4 s50<7/,O c`x2.YJM\b$|aX:xoD@T4ιnvB {$)R pwm8 F0zvؽ%kØd(J7;}4\>NL68"&V[#f~xAْ5oSw17`v&ʶREoDό/G=]F#mi}d+]ڒRXN#ǓA:T'dN|[hA^-pc;Nf:U2u'^>C`4N/ވ]^Q+I*z4:z`]]rˉq/ƐU<pVS}1evN!i {tiE;'~Փw 颧Kj\SvGIFӓ;9TgŻE Z^=X9@ًcHR˳j ~Ktf"@PM5=E{[3PboȆkĬm:qT?RE$-1ꮝ'H`'f A0UQnm> I7·֌.Z[HkMbc 0*6Hӧ°tň+q!P/.@iF ҿVߺq2w6Ui^O㋪ykbw-86a} uW:p% a+-_`5TmYK\9bCݜ=[nfl aEҼq՗6B드t'D,#f۝$QL5us,G@ε= hr8 YJ.79#EȸHrYK .ƽ/{Bp3csUTj&e۶^Y3T|Ԥv6VZ@8L츮9[K4NR,HvASMv8aE'I-Ȟ$џ/Yy2 #_g] \FЌo>4 Xb2@SkxgQ4ZD|_f$12\$V W{ׇ$=>s; T`H:lu]qXC~ Fm J5k{^@jN_;˽^Z,$* $o;|PHH;_+-z |&v*0$vP6{ `lIt.Wi,/&Dq$;{HPLx-9B]M-@ }w`$j4AMGa 紂w$( &cDZbByg/+jnIŗhz\7U"|]Yz[qkPV Z|7oƈ%Vԋ /Q 5I5ۜr4WT(+s,KȊ&Ï=o#_X\W̄'l)A 8`$Ro^uq`d_B hFD޸eFÈ[/~z"+Gbǖ7aQw٢XSLEzFͰC ~Pl'yZ@[lA+p3)97YZS/}p` P6_.|4cW| :=h2z;YC]sLmWreɩJW8e5 zl kH~2A"Q|䯪拰9܅t駫&h?7wQlO\md9v =6<5"m bIO.W[B& '\Va:<-DR0yN߃P7Xfi{ۑF& Hai7Q#xtU ]B42c)M}[ji4 1 ?_7.% tq%j̓3lM8/Ye^2LyϤ.Ԉw,pK=bUPߥ4u=T.daf,1t O< 6m ! pŠNZA=u+r6}]%)zR}rn:4^Jޭ7Tu.3r[t݉rbpZo/hb~kdh=)9'*ovUXHT'TkU/-EVaW QMIknL'z)I΃ܣ[8N@;(KD71:w%m'V?uɒxMFwgi>ē2D0ō˲Ѷ>+.asnlbqVYLS%NoiA*De>!Ŧ;+m &M\C"E^SD|fqxV>`3IU <+a|G[dE΢,iOqw:6?/L*>jRAY~gvz7 {L ~nw >ϠBI=To_Бזvިދ?OAcmCd)k]߁W&-ɺgE]PvZNE9UI enEQS91UcS8-n_^%=6&=}gnk'؂ Vf!<6nQ"yc/p*yOF=\vGr[CL ?;=Mt]l~>rUhsXQ+۽Џz RGrNS 6s@E%Bxl0 eY3&'Rֺ}Lg}/٘02k_YuግLg8J?1;`pJ[foiњpnbtyJN~걍QeC̙4AG3X-tT ߪxO~3Q0ctOVGP}5K24SHa^wdDAX1˘(9 < 0rI)xEr3@:dji(ءR #s*9֠@G},Z:d*o{V\,أլ#Dw]^Pd?=W?{ \ϏPǹpT1~<߫p~K bv#7F-u">3S`y<ʲfZE8m0Q(c>>㙃ٜ0C6:s^ʷ`cWa?f`+ !IcJc.+܊XKdz`'V_&,Ą2=0 +^K֐SsdD>($:5fk6R<:p_Uh;.qy)GX{sK*ieڮ3!1y@"/f{(2鿻Qp*j(;s-F'q P8*H?_ #.;~9̇&VO!P T" Bk)g^С4YE0'ߣyPw -`|Qwb4pAYsr|8Gk^Vl D@<,.fP ZB?0O.| #)Oc"M fi*Es 63Eu~`#$ N*1}JC/vkғF^:ajPTNo1y&>r_vt7ǯg8 rBC"= >Ë=d. ˮ%Zbe!<J ~$ϕ李`Zz7 dQHOřqxX[\ixWtv[[f$iF!C;:jҊV:{gh[q$#vm<ѶHוU;C=Bj̎.dYzQ {0T%)rDct.Y2sr=IO"C`{j-(Ta[AIm.gvzۄAمik`\|<9 0~5*xJ)֕\{j/:9}Uiʷ]6@I_uZ<@RL60'٠;NGaPwSX }14(>Ӊdح\YݪJKV&Y~aNǵRɻo鸏2u wsWp-1ƈ"'T@UAqdnT#t;g UxCwWc/(4GƏ]Ld,o{n̥ !ryQKP\ 3fGBȕ0 7ّMRH<뀩;:fCL4 n5SQ{z_*^APh4[+YB݉3 *0UgQ#2)jV:nq9Wv xc$mdH2)Tb /_vcݲ?pl+J~p4SYy#˝{VPAyQgoH@t^CxEl`Nqv Hɽ@6kv }'\J ?' R ٿamF4dDtr͢Sra19"З:}KG%ʬcXjZd7\>@FR c<>TMoh]/{hV5( *'ܽF=vv}5A[iEգX<\:;̜zٜ'auĢÈ)Mސ輆 giˣ%^[a&{ؘ/Zͽ 6&{o01wDZ.y,gc㡹ȲX5!Kn IH0>FɩWx *臑DAo9l!bcdEu3zߟ~^GkكX`;~JpX7ZРaS)2ch(][.uKb-hP|eȹsQSd+Z 9=2f -}%O^k.S5 Zj;PS%$ _vW5~4k q!mM8aj&Tغ|9MGWI-8Y? 2LHZp~d~K/GN3@qr!;`meh{ " S.H>IKBWJR7 >\ߍN|ۗbQDO^N)$dY,R7ODC8Ms 3N>pԏ/ u+L6wmgHA]V8 C7 KfzN9١vwJ ׼&Dz7NMFgyTrzt"y^֓U+J϶Le?pf×I|F6&M"0?^%8f, )T&OP@*^ y(~fŠǾ wxӊwQl97֫@T i+?'ޢm.7}b\Q%=ʴBaF) O2n|}kj}S:ڮ+i5[\gmW#xÑv"tqĂm$RJq }Z;6%ni?8Ȣo5}>O݅$WXtz`eZ!bE#MN;E&]$@uΐ#E %sCzI0դ!,L{`3t7*9=:Q݁/k'([ڌh,CPBʘ8vL}9B04ܱ+zCW K׌SjB* 㢱rnoWl}$괃s? ED 7.IU Gi%hRLEXΛ;G?~4E ,gLeœM;&bRoI j{:OI̟1R@"/X٧\X}( ́T8 {@lqN3KER?/ct›ԍU,Yt鞒[yAxa΢Fd!R&6Ԭt8ԯt_wV3P_ǃcV90#O֐wSh,~k=ģklwLY9rزG(m"9 Cc]/TUdžl'eL{_PS7oaIB6&u }O3s'vYc 2Wl/w~}xn|W 9b$7 MYd}k:q-j)mu( ֟˃qmg=9OÃ-[5\SO*szݕvDp,L>NMj*ZR6 &P@ioUL`x{UwlJJaό|*p+ Ѯ(^_ý%rGs4*߭S 2Zjmn]sW/p|fxX$KFº : "/d<9#C뵡K=Ҫ @N3;J7Mܰ4 | Q/7<8yNX>Fj&R16 ?r(8_txڮ۝hAآ)a SߋFk,ӷ=GKl$- `7NJfu0qjև\zEhT}.3[# ) J{`-xj>^ȫxkrFz9Ήd:ų ;R@6PL'ӗ~\&)QەLőDp?rY^ ƝW>vjnF8yfH# ī֋(.ògr\ngW+w_ba( d؃^ߒɦ_y wx* 8Gu>d3<,1-5d좉ë%pI$z*SX7xs%򮐴0Q4C\[ y2wTe{h _LU`6V4"Ӄ}+QϾ4TH BUD;7t,<z /VC#qk5g )u~_xfwvSgfvnWc2 +_8-?bћwP=kUMY+ dm g+rMk/woT1 yѭ~ȕv|I1x;ቆoIG9 סd}kBHً.؀\~49p#ZTaVP]z^1K\β?XN<7SI>_<0%^1Og7MN c V]',#hi4&ˮ0Kq g2%\Kz0<5,Z=]b +]R!t&G"ˁSMXI y|;td"8G&Fu ߘ)I.~_hE5J[{O-0P&0<6i%➟J36uNжW茯4c|plQyU@R3Oʡ: ;ENhY' UZJٟҳ.;ɳBH@Z]2TVMNe!$ -LRp94 n8fWtv7&e܏Mf;EH:FRj!T?`bZ5[k٥ *g@al|cӹW~pA|?@ xHᰛ'+~ә1Wq\Ks÷zmg"(qȉ oIʙB?H+]EjZP`b-J|޻8rũ=m,<4%ܮdo1sP\EqW/Tc9C߄Ayng[4IszSC< vt QWBEoasPD*_%k{^'h[rイB{LD U8ib>%I_? <rYƺ^kob-%iE2qK)V]pZ~a Yt!vLc bv+Q9flo/c ME[=Arhm5Y.DH7J Bʳ va6X20iSIJiU '^L%eh)n:w-*t+(bϕo0{h'^u` (M)3-$NUtDm,'qe"gKŔmq,d nXA>MyB"$%it!e;{#)N53uO [u?. @G!0v"68tif u ߙ)h. 7B싘WlL1cfNx*5("ϲO-֓1 1DPoS\fF0R\o1(:2TA ¦#W2\vQ$!V":F)Ku~*PpaSww{-5x`JbNV3$#)5eC#/szr]Ii ԍ2:"G}dӨ˒/&|~}4f"ˌ,귓tIy>?߈ApI%цFw^>_8ƒld`Ҧ9nB[yAHQV 2KNf:F;'3]TV#[J'`h{5X K<$XNDEi}˂C6s&ζhV,OsC^M=+Z& }u R;:}3盭(mtѕ!|Q܌p/49AhrU)e ({&Tj PD]Zoab/ƌ !QHY=גҝkdO0 ܄uCբ2Њj6*SZ`#a]VC^Qƀ̴Y.ϻEZնjɌG&R"8* \0j1XP-|Jپ~2[s.5O,(cFRjf@$bZ jȳUEJiAyDUXM} HwATnXeqz!$pl~Y4Gq=grm$Uѝ NbḾ=,V'"^O5BAk[.=Ð[AҢDYBy|Rxf?6Le0'%32ү9L cl`w忞2 ]*a4죦3Fu:VSw0_1 %X>o}R:\!qhDKe Gԝ!ML_Ye$Ubl@aPOEJHQ86G@?tD>CK)Xz<3n4xp򷹜|cĴ@S6a Xx:^1F̷֜tiQ"͛!v:s@}E;R&eiz&60j{yWHZAn.̀B`Dlp7`{ˉ'1iD?Ζ<3I ;XvtFG>86=CH/a@. ]gY.?Q8v nrsA.OHn=[B>ЈI?8@ye;:#[ Th-Ӝ],Ѿ@{@4Pk%`җ<{qч=MiMلC4맊4jslDUץ%.5wFnB% \Fjljl[vw2mqZeG -3^^!ܨ<*6SqVϯ pP[ǍBE ܄ JS( 0656̰;B^-rQ6>S~lzFm_ kx,xYfy2D`O`.x WP kuVp9sj q<<bs3|p"vڟkߡN\) 8; s-lƆKZ%>VAfAK0%̀/AUY<&[&x:E5b*Ѡr "  kyg""SJRBH3Mj+z4b)u y[. 1$}sJ;WfXm!/o'A'^08.>>H+74a! X&ʖ@AuD A'h!iOi |RPHr+!)!l߾.ol|6_\XMh`$I } MşLW u2nqe}LIHxjjaNϵ _7!d .#5L ^LLAt_gOjIGIA/PG蠪$ :8nᎷH'tp܋c@r1/zKKp2-! >AswpoĜ]m(^YX*ђ-E+tnnߔ%Y/PoC6=#Kp p#=lWNHrrj$SiI]KD2&/CgxGazB\/{G#Gf*`&;oBE^E*cy Ѡүq:A]=h׽ƀ>)q($b9^ PُshY~ST(4?V*;';| EUYoȫoWjGN˂Nݼ`/p9\i24H749:EM.(t3tci@.hNϜe_2C1}p}LU̢axa~:k>u1mk1Iv+| "T[{%iDѨM>Jd]dFǗ}DV!׻רO'\*]:#fAwn);:~:йK 4'(NB8gQu;Nxqev~yD~rހ 7A5vNev/?Ù{Mk33oO{clˊgUtz*m((7"<#~# ['f69 6 o2XھC[p:X)BRB}Y\6cC%)!z$pwo^FI3_zm_i8I ,00o<ʾk(߭&u$ 4&I'J\:j,y!y.S ꤗA0U [---Ƿ]cTqڱeɪ~XjML9 J }Hī[_v:q0@Ws{\{bHv2aS}mC'HLHgZ^il._ʎ1Zfgs*KK _6)f55@D' ST@Ia.M "Cgȅ U?P,5ǜc&7񉧳ZOϷVva҆ A *<*.J|}?ī8L񬭞?"?n9o@B]9ZJ95W"F1jyßaϵ:(/ #sQ$QԚ5OUr WHT[e7zzD@OJ|G|1hLQiZQ J9J1sJ+Lwzoww =]]ۣFr?O/Oh, LwI|?hsċRTa{:D1#$RhRpwY坮{%%M&ھ@a=7w !Q}==" ƣX%> X"XB N4tWU0Un!эQs]i;[V{xd>z$Q*c{>@njhnh-ɑr%t\PR3YĹ`c@g886'$YuQuEt#5`m2mNGI?T$jmhv @6bCED:D U+f@N)|/O}smŨ (etbFHSz54} ; Pq| ^8??hSm<ڣSݒ:ݸ$]NSŕ}_[MrvCCﲭ}$ne;n'F/Qg1/r1 ‖r{o_az-'@‡NN!k._Ć 411E ~)1y{W6=٥$X/>LʀCDaeue{uHZ^OZpa鍀iĴN>98\M\UvIJﺩXcKKpmĕ}! 3!戙LUߒ山+53 c0<2< FKBfB)$&RR g-kIXxag7y3YOlO"ccHzH[0P7$̄e!!8Z֒%?ea e眮@.^r$W@HH( j%jYPoJrI\XSS'ˀ1ϠAm((%pu oA௣`9c m B=rrUe/ YF ǘ ͘+&&3wTooQEO0J߲55"уyC DDܹs+axww3%Z)JJ4;tB43 O,w, Z+;Q37Q2)"Y+AhBi~ȠK 22 eH'3H: -2PZZG" 8ݐ*dm* SQhvhf5OVvfndRDW}5ոƮ ::Ow&iv&γ k΅JTGVGSaa hLa'' ϝ-·T6]]t!C]w]쫆A܇ȤQQEJ {2qR뭡I~ubY],a8toKp))rtIɌA;7ݐ{W{nn-ۼF U{lǥRn4HAA-&8}MU@YV{LLCf뻚jL޵D`Kxdx@]kԐmo=Co7kkI?[i0{\ cfDo)lM/imqsG?YzA&W2(b2rT0Fuy4um!?\^19*SON[=J2=1V'̧s%OO+N兀5c 5G͜(BրvO\- w(>~~/4Emm把,0/TT1 h6Lk$="~ЀMT 9 Dcè6@@r7m_l%]a*FI2ڣ ~ -|NTGNW RW]hee/92"ߝGGA">ɵf`^SwՑ**b0fCJC-``Kͺ@eקZ sEE{ ytyHjHŪnyiZur|GD| #Q̕("+#"0IeJƒT=8wpp0ڿtDk88[Xh 1PCss+5fR!H;;kYz*nq֒~zdzeb n-E^%%]T^V,,QAN9μt//.HvKN77^qa}:: B 9(ԝC( Ș @rPkPSIyTXN4&NQj> <3p3(||ᶐ 5` 2&7JçJ/[*Rا^YYWt͝#UUd \`E`?b6sҪs`llz..__#88jfxB7q$\LdPdu` \cWWAtGA&B&z70|%lqB@;EY.ElnxFFERX4*,'p'yy5{' ^lg^:jBDUUxWJ܁/6/5{{=: ##,=?>ߣXX\RJ-j6G_d}D?}ݎxܤ~ti.5iX4_R?: H,LsZ.d=<gD?;;p~~Ц6<Kqq|rp /XGXU+.)B%5|\\a?h@u@*}`ǥ9m;ZY1T++;RKGnjcMM|.0W0M 1Q߿S7R7b}}M#5$k`k}jjUnkg!99X݈""nW 1-Χm?>II'*h˔Z=3lG,<;IBvvOys(U ob]PPJ砗,IKIMyLLn`J?V?dv8F>>[o +]{ 2;Ysygw2SqSddCy:{㇯˔2ʐNf $$tI')4 iU 066=NXz~|}훰 釥ժaads4 Qۂ vE@qbZq'_;`gg%iiпuN ))lM$-/s-8 ы=5+<S+WKzz~(D >p g3X#[7W[ff!"nHQ[Е44j g(oQOTC"8BKHpll'ޖR 'R6i6YYՇed%9., ѡttL0\jҰhl4g(xq==?(--i3A%~00ć]UѴkzf2؎|x)f}efP̣կܟI.[[bi*•o+[ѡ}4j9Ei9O1\KϻKhbb:4|c&Uc[hhfF ņ1UСLZ/LRυ]lUHX VHrg\EX R1{w; ;(Ph;7ZKTsQOSbOǮpu灸mρ% bFcLB=FŤ(1a1O52"NA"wlZMcd(cuAߪQCR汩NiR$$~賜yY+ =sDHcwײ퓜)7PTf7g \7|tyW&+V Õz滈&(<7sn ]c6\ꆟeR`RDٚ,AX4S4F*7?.o\ۦ26#X@%ԧ%4EE LkO}_t̼[]hEbS, j]AՠiąG>_+$Vf9.,f :\'@Q*9$//[1b#&!v#*Ow#wCPmNCrnr0!-о tYMn[qtIiKɁ|YK(=i|{6bk6A7 7]ɺت<8>6!ԷkXnv^X%G֌50wZ0HNJ@KƘz>mt\\ [԰e m! MpH=z|u |TI#d;v<_ L7׎m?'(?a0( _ףB$h؄֙o{RʻJoju`aq3`6-E+*2)W@ :?c-^@o`ʥGp!I@/x^S%S=^q!BxP@F0e"٬4ka,[_(j3e(*3T.>:$6a;!WK!?F\r8poJ~7erK$H^oaA^Ͷ=⫼bpNhx~U͵grBN EFݘ@Q+xGLx4OxEy0ao}vM,Y[#DCRXwQUQ{O&P:MvnZw&VTdҸ^|a Ԩq$.;?+`Wq5&oG9 0 L V4Β_fȃ+'UqI.CTn~\޸]3}"8](k$>#91hm\u'JEͪT'F}R9jX_i]  OTe)F렓{;7o<fDpPIPbb „0`;lr˫Oh&-&y )UXCB )s 3soޅ6W﷏.|[ js,r ?*+!X _~TV_/yyVa#F m> v:f^sI4sM{qXh hsb;u2;YY&`Dq h֛Xn2YQAsixApfU!/lXt A~}bT:t.z.eG˅`,v!ؽq[θ[!4MgIA}3d=I=9db7v5v4.ۑ%ÇU*#Ib kL1 {Pԥ{Qf&)-VZeoz"zOA%%h_:7:SVv?[n1xj,7;/,bdÖu%Ul+uD +': j+ 8Q-ă'.MCy֞Z߮fySsۡE2o.s uɊd) R,ɝːcSM2FMQKu6dlqFqJk5]jD1C2H|w%a) FiarbRcڈJ(4MSʛ*:0*k_ݟRZ C' q]/n>(0jI}kFډ-|$'QUsgZW̓n5n>jTwdj{8i$=>Wr>]*ž[/?`'=[ UO\5*ڍ2 5ܢ=I-O68: aB 06^9 "HC9%H&5x'MY`P)mHxDYp5ZTh"V<0 <"ͫYzy8z-rg"DrEJ;)AE JV39B| _6-ǣ2e{2:,L2OسPxn&lnm~[NP~cq^18'4[*}iN}XAKO6"|!>媃δ9gvg{Yc(4-q*HlvGB/0B~ 0Sejce|8fo |og b㻡GǻJq1PyW gQ2Pz1h;wK^>|P1`Ԓ"3JG$ygG KLjN#׳ y;?9rIQIuC)j0Lc)"DAJKJcitN8Am8TS뎋HuΎw FGC5ػyZ#ZY`y-m;yXu6XP3 Ҽy3™;"~ϹBBBzeqK4]sqgFFSF9qTctJJJ^yRblVVV&,(yyy΅U57Ueػ';n\l-兿a wH6i,0\ f6bb/b'8tOx8,\_ۏ!.3111>Yl%QmmmBp螉!p..'.XUHd%wJFӼ(MJ{=ŮT?Y'4`A=ľȲ= )uAN)000)1c6+++{ͯ#i0/vReAnbHA嗗ΉBo0 H6+GkZplX:`l^N#DzRRRAB #\\N\ۊ'eIM|s8}ް+ʶxލG&=a@F6ժ3ΎD0*HHH< #YJ K#]&&&n ԎCW @LD/ n̠Lzˀ߈`(y)TS3x@MBM$222R䘪x"A7OfP91ܗW ޭi^DannnNuW-^DD+D[x {ssUsxgZm.MS0\Ru__ _SIXS#;(rkb{71d$eUO?>o44:4fb;bE$N h D$:OL5IX#2P_e hejj j p{y]T{S]Z}}}y&ܐ́&}--c-af*J,D!$εe'̎!"5KMTXKI H,@H999t@Ї.y ԁ^y9ȍȋ|pjΌt+|xLb_dY d+ }ѕȩ+Js3)7V66B6X>جoXzvvvI?{> IQQQReC'#Ç;)C'hwL'EEEB7$莚h>x 1ppp(Zt'PZqiXfֿ&WfFҐ%%o7Ӽlkcuuu Bh.ΌrW*c\Wkk0k]va*,gijš囌ECt i"=)))vqfR>YYYi7ʆN 7 [ FE.O.BEvi%i&9h.wM5CCCZ`%9꾾  2>VWm3332dA@ޖ^d9x~EsZEKk,,_,+CF:yK)ndwގvK|v8ܝݸC72\CGGoGYPoz]PE%(}Qm=ϟ(XXXV jnʵso^rk_^b. ׅ,@@@ !hoڿ jK>G_mjGEO g &z{5${'PPPumjEgHN+@0﹏+b"P  ᮮRc{Kl!Y)rF}FLLL ;^3)BX AͨnH| aSVa  ~|hhtha6AAA$b u_lido4QӒ555Y4m5III\gfG{{v{2 oK/2<`?\/&\[߯D]*V#`qVDAQP<~QD`f(Rn?ɬlll"0zq*Eֲ>>a>#NЫN.sł,rC7w9J1A i1\è"磌"NNpNp$2z󥪨ڨlMU{!Ô;3kuʬ3#iiHi2ePqzIdʵ/"""W}^^6^pQzvf?&Ǝ%#^%ܕ[C~.PT9;9cM`J}-`aakaȲg<<85gF:>< &ҳ>J-k L]]r]ЕYIF`^Yu*n:u%%@%~/P0N < Ұ2vUUQUVzqajO??]?yr WW)W&у`jG" Gd$$|$c6\!*SꮑZ*V1 .8eee4[޹.)[皊~t ]=;3AIcccGME?: Fvrm v=5r>?0%V0ѢdLU<{YLZZZ3(SW6 9j"s@jwww<JuIݟxHw^xR7_yl"7q :԰.LX"T&AdfBbDp,Jc]ЇsZi+ⰹI:\ _4wr/G~cS.Y+mWB7j `rýN]W Hq<^pD+O^\[rM-=~ بTnvNBmӆz3-?(skU|S=bсs!-?87#N( `-0\riΠyJy!pe^(iS%JE^k#ƺ ]aA)Htyi2Zz3s֪q9)"6G@ cwAkH!YMi4c%[GF{0Nb't3ug~^C5`> l@\I@*_9?|lT*VTm<@Uv;3.l'`SL Y}g^U:V'sr0.SC]X $Z_ >MFVCͧң /V+.;*H}+IT>L67NƘomUH녵Z.FO@"±I¶5,PuKasS$vPp/8FŨbWa[u(O ó6Mo65BUWV[;2:w>}×%̯^.<$]rzLf 9 u=e& `3uBʼPҦJi0/58iSXȩ u' b29#jefZ~P-Mx vf=%wɼvyߌ~!%j-ai WiaƦ\Vڮn}~yE"`>M˸509M'TFSg[lNa2 iOH#R@}n6z'R|^#~ȏpXWcN&nFuP! Z`3nZ2بlPڅ^t;cG;y:xO? X.+h[4QCٟr賒|IX#qD{2LRNDe `iVKeľ _z PKuCiĻC;e(8#5YexwP=+K{fZ3)d ;(fY";XA9ƺc0R SL+ab_Kon Qyk9_FODtI >,< s4yL ܌B4ChJIUwAxz,Wa4C|i`N 'Q"Ӥ Zb 8/ >G#oTQv) 7-FDlT`6rgb8E`?,+(Hgv $&|fܴmdsC؝Ϸ֐ABQR&C(Esq-+vPA Dm$; zɮ{2 ТPwNljA^cBi<ӓ8"eF/׭6ϳ9joJBΪju8#;j[Ӎ { s+@U_(ņVfZb8M /!kb `pFY g~|ywvA!gUf5 QlUְ.Yw&N <7$ΓSbQCh0|. +%Wۼ=)غZ1 4>_CmeHY6W9.V>W;u`gdLTw'|t.0Ab|$nPͬ4EԹ>_ia(0=>~Ma+fD&CMJs `D|R؂JU $@pՕZub2_v\)pO 1UX/S+48[Mr7Ԛc_F$]ۧ.4_d;3?'fG# دb /43EM хI,i_j#}Yh SQs-dޅ`  } Db\JE-K/ ܳH{4j"*:̓:eaty 9[Œi*s98*}E!O@^h9F OwzL. *![n/X&=:VGT UÖjbΊўyӝIߣ?vOș͘-6rI )copQMv)44usQ3)>9VY6V'*-},g? j0+EdDY lv5ךXb#{87\}\Hp:֡Y+ 6Ÿ,Pr"~\+ue-Plq!G9ԮZ$JuYzw!Rsp Dʌ^ml]r1g>k7c(:UsaFH Ȟ&t[١3!}`)Z֌ t;O\쉇.y!%;3pExݛc-?D{ow]sxKv:, {wבeK3Dz{qn$dOwA:̍XɎ22o0 \uI,HGiS#0$/VW=ItX+mdz fL}5'Aug&{ƽE_VCw5c4xI֦HUc{CWMyBXJ " ֈ4#2t@ ٸп^SP| 4ƄoK$xe&C"R72f%؎ i0OYMKZlGR?!>'q@g`Vϊ:Ȉ؇9y8Z~,F6`H^>wz撚3l²TRiN*4QHGXc0Sґ2&r"ޔ xR^UCܯ{]im?.ulN\`ŻG(?3"YIPힲIq^|]jW-GN0.ƹ6Dv53*>:^a SBW6aY*xRJ,f'] a 8d-ۗ*"G j9?b@hOgH LDl1뎀cϡt5',e~TЌi^Pr,RZ_FUao?*hF dյ]/6M˾TkX ڌW[mҜ<-ThG 2Sy;YO†̥.Ԙˬ!}z댨qB b_څi=FT!I1NίmՁhԋ1DTt05iC2"6Q;聝;PpVjef)'aɒ$XxAݖ]aCfs<(!A@L#QOh fSR| r̄7CEu,F]N[6JKqt7h_@z05%8`7Vhp!,%nk">V@)lA%IXMеU#Wψ[?\e V^zِ֝WJ hܴ#"vG[7Q;96$=\BJk]tSӧ$sr |5̈L}$v3Զe]7-I1^Έ;QJ.LN0 ėQTvʼ?qc"[;'Rb*;e^X́.+/=IN! _ySϠ X[2kwZ^{-Oz~t J޿)w&WqخgxZЎdݚNt=ߺpa^XajE :A~q+*u K51Qg%[X7纜*l-tLCd+@dM,1. Case6k[C(9)-fM#&%]̦@Kȵ!|<)%(?KrpTB"Лߓ.$8hkK|;L 2ExnH}_oِ lOx벭yiE,R _ou"H=OyYB47m0K"AU^L6ztaۓ@w xX :l&|2I5Rs TDx܅c]+vFDM': 짭]8E`\OljfωpGzANWbo y(6z}S\(TB90n-@kF:xKP1~7ׄûDcm8='1lkuznyechik-0.8.2/src/sse2/mod.rs000064400000000000000000000122101046102023000146030ustar 00000000000000//! SSE2-based implementation based on use crate::{BlockSize, Key, KeySize}; use cipher::{ AlgorithmName, BlockCipher, BlockClosure, BlockDecrypt, BlockEncrypt, BlockSizeUser, KeyInit, KeySizeUser, }; use core::fmt; #[cfg(feature = "zeroize")] use cipher::zeroize::{Zeroize, ZeroizeOnDrop}; mod backends; mod consts; use backends::{expand_enc_keys, inv_enc_keys, DecBackend, EncBackend, RoundKeys}; /// Kuznyechik (GOST R 34.12-2015) block cipher #[derive(Clone)] pub struct Kuznyechik { enc_keys: RoundKeys, dec_keys: RoundKeys, } impl BlockCipher for Kuznyechik {} impl KeySizeUser for Kuznyechik { type KeySize = KeySize; } impl BlockSizeUser for Kuznyechik { type BlockSize = BlockSize; } impl KeyInit for Kuznyechik { fn new(key: &Key) -> Self { let enc_keys = expand_enc_keys(key); let dec_keys = inv_enc_keys(&enc_keys); Self { dec_keys, enc_keys } } } impl From for Kuznyechik { #[inline] fn from(enc: KuznyechikEnc) -> Kuznyechik { Self { dec_keys: inv_enc_keys(&enc.keys), enc_keys: enc.keys, } } } impl From<&KuznyechikEnc> for Kuznyechik { #[inline] fn from(enc: &KuznyechikEnc) -> Kuznyechik { Self { dec_keys: inv_enc_keys(&enc.keys), enc_keys: enc.keys, } } } impl BlockEncrypt for Kuznyechik { fn encrypt_with_backend(&self, f: impl BlockClosure) { f.call(&mut EncBackend(&self.enc_keys)); } } impl BlockDecrypt for Kuznyechik { fn decrypt_with_backend(&self, f: impl BlockClosure) { f.call(&mut DecBackend(&self.dec_keys)); } } impl fmt::Debug for Kuznyechik { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { f.write_str("Kuznyechik { ... }") } } impl AlgorithmName for Kuznyechik { fn write_alg_name(f: &mut fmt::Formatter<'_>) -> fmt::Result { f.write_str("Kuznyechik") } } #[cfg(feature = "zeroize")] #[cfg_attr(docsrs, doc(cfg(feature = "zeroize")))] impl Drop for Kuznyechik { fn drop(&mut self) { self.enc_keys.zeroize(); self.dec_keys.zeroize(); } } #[cfg(feature = "zeroize")] #[cfg_attr(docsrs, doc(cfg(feature = "zeroize")))] impl ZeroizeOnDrop for Kuznyechik {} /// Kuznyechik (GOST R 34.12-2015) block cipher (encrypt-only) #[derive(Clone)] pub struct KuznyechikEnc { keys: RoundKeys, } impl BlockCipher for KuznyechikEnc {} impl KeySizeUser for KuznyechikEnc { type KeySize = KeySize; } impl BlockSizeUser for KuznyechikEnc { type BlockSize = BlockSize; } impl KeyInit for KuznyechikEnc { fn new(key: &Key) -> Self { Self { keys: expand_enc_keys(key), } } } impl BlockEncrypt for KuznyechikEnc { fn encrypt_with_backend(&self, f: impl BlockClosure) { f.call(&mut EncBackend(&self.keys)); } } impl fmt::Debug for KuznyechikEnc { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { f.write_str("KuznyechikEnc { ... }") } } impl AlgorithmName for KuznyechikEnc { fn write_alg_name(f: &mut fmt::Formatter<'_>) -> fmt::Result { f.write_str("Kuznyechik") } } #[cfg(feature = "zeroize")] #[cfg_attr(docsrs, doc(cfg(feature = "zeroize")))] impl Drop for KuznyechikEnc { fn drop(&mut self) { self.keys.zeroize(); } } #[cfg(feature = "zeroize")] #[cfg_attr(docsrs, doc(cfg(feature = "zeroize")))] impl ZeroizeOnDrop for KuznyechikEnc {} /// Kuznyechik (GOST R 34.12-2015) block cipher (decrypt-only) #[derive(Clone)] pub struct KuznyechikDec { keys: RoundKeys, } impl BlockCipher for KuznyechikDec {} impl KeySizeUser for KuznyechikDec { type KeySize = KeySize; } impl BlockSizeUser for KuznyechikDec { type BlockSize = BlockSize; } impl KeyInit for KuznyechikDec { fn new(key: &Key) -> Self { let enc_keys = expand_enc_keys(key); Self { keys: inv_enc_keys(&enc_keys), } } } impl From for KuznyechikDec { #[inline] fn from(enc: KuznyechikEnc) -> KuznyechikDec { Self { keys: inv_enc_keys(&enc.keys), } } } impl From<&KuznyechikEnc> for KuznyechikDec { #[inline] fn from(enc: &KuznyechikEnc) -> KuznyechikDec { Self { keys: inv_enc_keys(&enc.keys), } } } impl BlockDecrypt for KuznyechikDec { fn decrypt_with_backend(&self, f: impl BlockClosure) { f.call(&mut DecBackend(&self.keys)); } } impl fmt::Debug for KuznyechikDec { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { f.write_str("KuznyechikDec { ... }") } } impl AlgorithmName for KuznyechikDec { fn write_alg_name(f: &mut fmt::Formatter<'_>) -> fmt::Result { f.write_str("Kuznyechik") } } #[cfg(feature = "zeroize")] #[cfg_attr(docsrs, doc(cfg(feature = "zeroize")))] impl Drop for KuznyechikDec { fn drop(&mut self) { self.keys.zeroize(); } } #[cfg(feature = "zeroize")] #[cfg_attr(docsrs, doc(cfg(feature = "zeroize")))] impl ZeroizeOnDrop for KuznyechikDec {} kuznyechik-0.8.2/src/sse2/rkey_gen.bin000064400000000000000000000010001046102023000157460ustar 00000000000000nvrlHz]'݄܇ؐN y%؎ i0O{ s+@UomyQ P/сJs `聝;PpVjY6V'*@dM,1. *>:^a D|R؂JU -},g? 6[oG@{ QMv)4?!>'q@/l, љ5NTA^cBi<ӓ5ךXb#{87\"~\+Tw'|t.0A:UsaFäysǔ牣 Z1 4>Z:X4 Ci=~I,HGiSl1뎀ck]tSӧ$sr&A1Rhk̄7CEu,F~B|%N9(#⣀ۧ.4_d;3?'^XajE kuznyechik-0.8.2/tests/mod.rs000064400000000000000000000042451046102023000143130ustar 00000000000000use cipher::{Block, BlockDecrypt, BlockEncrypt, KeyInit}; use hex_literal::hex; use kuznyechik::{Kuznyechik, KuznyechikDec, KuznyechikEnc}; /// Example vector from GOST 34.12-2018 #[test] fn kuznyechik() { let key = hex!( "8899AABBCCDDEEFF0011223344556677" "FEDCBA98765432100123456789ABCDEF" ); let plaintext = hex!("1122334455667700FFEEDDCCBBAA9988"); let ciphertext = hex!("7F679D90BEBC24305a468d42b9d4EDCD"); let cipher = Kuznyechik::new(&key.into()); let cipher_enc = KuznyechikEnc::new(&key.into()); let cipher_dec = KuznyechikDec::new(&key.into()); let mut block = plaintext.into(); cipher.encrypt_block(&mut block); assert_eq!(&ciphertext, block.as_slice()); cipher.decrypt_block(&mut block); assert_eq!(&plaintext, block.as_slice()); cipher_enc.encrypt_block(&mut block); assert_eq!(&ciphertext, block.as_slice()); cipher_dec.decrypt_block(&mut block); assert_eq!(&plaintext, block.as_slice()); // test that encrypt_blocks/decrypt_blocks work correctly let mut blocks = [Block::::default(); 101]; for (i, block) in blocks.iter_mut().enumerate() { block.iter_mut().enumerate().for_each(|(j, b)| { *b = (i + j) as u8; }); } let mut blocks2 = blocks.clone(); let blocks_cpy = blocks.clone(); cipher.encrypt_blocks(&mut blocks); assert!(blocks[..] != blocks_cpy[..]); for block in blocks2.iter_mut() { cipher.encrypt_block(block); } assert_eq!(blocks[..], blocks2[..]); cipher.decrypt_blocks(&mut blocks); assert_eq!(blocks[..], blocks_cpy[..]); for block in blocks2.iter_mut().rev() { cipher.decrypt_block(block); } assert_eq!(blocks2[..], blocks_cpy[..]); cipher_enc.encrypt_blocks(&mut blocks); assert!(blocks[..] != blocks_cpy[..]); for block in blocks2.iter_mut() { cipher_enc.encrypt_block(block); } assert_eq!(blocks[..], blocks2[..]); cipher_dec.decrypt_blocks(&mut blocks); assert_eq!(blocks[..], blocks_cpy[..]); for block in blocks2.iter_mut().rev() { cipher_dec.decrypt_block(block); } assert_eq!(blocks2[..], blocks_cpy[..]); }