hkdf-0.12.4/.cargo_vcs_info.json0000644000000001420000000000100120310ustar { "git": { "sha1": "1ac16e8b9d4ee7a67613c9396c6cc1327652eaba" }, "path_in_vcs": "hkdf" }hkdf-0.12.4/CHANGELOG.md000064400000000000000000000046251046102023000124440ustar 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.12.3 (2022-02-17) ### Fixed - Minimal versions build ([#63]) [#63]: https://github.com/RustCrypto/KDFs/pull/63 ## 0.12.2 (2022-01-27) ### Fixed - Re-export `InvalidLength` and `InvalidPrkLength` ([#59]) [#59]: https://github.com/RustCrypto/KDFs/pull/59 ## 0.12.1 (2022-01-27) [YANKED] ### Added - Ability to switch HMAC implementation to `SimpleHmac` with respective `SimpleHkdfExtract` and `SimpleHkdf` aliases ([#57]) [#57]: https://github.com/RustCrypto/KDFs/pull/55 ## 0.12.0 (2021-12-07) ### Changed - Bump `hmac` crate dependency to v0.12 and `digest` to v0.10 ([#52]) [#52]: https://github.com/RustCrypto/KDFs/pull/52 ## 0.11.0 (2021-04-29) ### Added - Wycheproof HKDF test vectors ([#49]) ### Changed - Bump `hmac` crate dependency to v0.11 ([#50]) ### Fixed - HKDF-Extract with empty salt ([#46]) [#46]: https://github.com/RustCrypto/KDFs/pull/46 [#49]: https://github.com/RustCrypto/KDFs/pull/49 [#50]: https://github.com/RustCrypto/KDFs/pull/50 ## 0.10.0 (2020-10-26) ### Changed - Bump `hmac` dependency to v0.10 ([#40]) [#40]: https://github.com/RustCrypto/KDFs/pull/40 ## 0.9.0 (2020-06-22) ### Added - Multipart features for HKDF-Extract and HKDF-Expand ([#34]) ### Changed - Bump `digest` v0.9; `hmac` v0.9 ([#35]) [#34]: https://github.com/RustCrypto/KDFs/pull/34 [#35]: https://github.com/RustCrypto/KDFs/pull/35 ## 0.8.0 (2019-07-26) ### Added - `Hkdf::from_prk()`, `Hkdf::extract()` ## 0.7.1 (2019-07-15) ## 0.7.0 (2018-10-16) ### Changed - Update digest to 0.8 - Refactor for API changes ### Removed - Redundant `generic-array` crate. ## 0.6.0 (2018-08-20) ### Changed - The `expand` signature has changed. ### Removed - `std` requirement ## 0.5.0 (2018-05-20) ### Fixed - Omitting HKDF salt. ### Removed - Deprecated interface ## 0.4.0 (2018-03-20 ### Added - Benchmarks - derive `Clone` ### Changed - RFC-inspired interface - Reduce heap allocation - Bump deps: hex-0.3 ### Removed - Unnecessary mut ## 0.3.0 (2017-11-29) ### Changed - update dependencies: digest-0.7, hmac-0.5 ## 0.2.0 (2017-09-21) ### Fixed - Support for rustc 1.20.0 ## 0.1.2 (2017-09-21) ### Fixed - Support for rustc 1.5.0 ## 0.1.0 (2017-09-21) - Initial release hkdf-0.12.4/Cargo.toml0000644000000024060000000000100100340ustar # 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 = "2018" name = "hkdf" version = "0.12.4" authors = ["RustCrypto Developers"] description = "HMAC-based Extract-and-Expand Key Derivation Function (HKDF)" homepage = "https://github.com/RustCrypto/KDFs/" readme = "README.md" keywords = [ "crypto", "HKDF", "KDF", ] categories = [ "cryptography", "no-std", ] license = "MIT OR Apache-2.0" repository = "https://github.com/RustCrypto/KDFs/" [package.metadata.docs.rs] all-features = true rustdoc-args = [ "--cfg", "docsrs", ] [dependencies.hmac] version = "0.12.1" [dev-dependencies.blobby] version = "0.3" [dev-dependencies.hex-literal] version = "0.2.2" [dev-dependencies.sha1] version = "0.10" default-features = false [dev-dependencies.sha2] version = "0.10" default-features = false [features] std = ["hmac/std"] hkdf-0.12.4/Cargo.toml.orig000064400000000000000000000013101046102023000135060ustar 00000000000000[package] name = "hkdf" version = "0.12.4" authors = ["RustCrypto Developers"] license = "MIT OR Apache-2.0" homepage = "https://github.com/RustCrypto/KDFs/" repository = "https://github.com/RustCrypto/KDFs/" description = "HMAC-based Extract-and-Expand Key Derivation Function (HKDF)" keywords = ["crypto", "HKDF", "KDF"] categories = ["cryptography", "no-std"] readme = "README.md" edition = "2018" [dependencies] hmac = "0.12.1" [dev-dependencies] blobby = "0.3" hex-literal = "0.2.2" sha1 = { version = "0.10", default-features = false } sha2 = { version = "0.10", default-features = false } [features] std = ["hmac/std"] [package.metadata.docs.rs] all-features = true rustdoc-args = ["--cfg", "docsrs"] hkdf-0.12.4/LICENSE-APACHE000064400000000000000000000251401046102023000125520ustar 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.hkdf-0.12.4/LICENSE-MIT000064400000000000000000000021241046102023000122570ustar 00000000000000Copyright (c) 2015-2018 Vlad Filippov Copyright (c) 2018-2021 RustCrypto Developers Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. hkdf-0.12.4/README.md000064400000000000000000000055721046102023000121140ustar 00000000000000# RustCrypto: HKDF [![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] Pure Rust implementation of the [HMAC-based Extract-and-Expand Key Derivation Function (HKDF)](https://tools.ietf.org/html/rfc5869) generic over hash function. # Usage The most common way to use HKDF is as follows: you provide the Initial Key Material (IKM) and an optional salt, then you expand it (perhaps multiple times) into some Output Key Material (OKM) bound to an "info" context string. ```rust use sha2::Sha256; use hkdf::Hkdf; use hex_literal::hex; let ikm = hex!("0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b"); let salt = hex!("000102030405060708090a0b0c"); let info = hex!("f0f1f2f3f4f5f6f7f8f9"); let hk = Hkdf::::new(Some(&salt[..]), &ikm); let mut okm = [0u8; 42]; hk.expand(&info, &mut okm) .expect("42 is a valid length for Sha256 to output"); let expected = hex!(" 3cb25f25faacd57a90434f64d0362f2a 2d2d0a90cf1a5a4c5db02d56ecc4c5bf 34007208d5b887185865 "); assert_eq!(okm, expected); ``` Normally the PRK (Pseudo-Random Key) remains hidden within the HKDF object, but if you need to access it, use `Hkdf::extract` instead of `Hkdf::new`. ```rust let (prk, hk) = Hkdf::::extract(Some(&salt[..]), &ikm); let expected = hex!(" 077709362c2e32df0ddc3f0dc47bba63 90b6c73bb50f9c3122ec844ad7c2b3e5 "); assert_eq!(prk[..], expected[..]); ``` If you already have a strong key to work from (uniformly-distributed and long enough), you can save a tiny amount of time by skipping the extract step. In this case, you pass a Pseudo-Random Key (PRK) into the `Hkdf::from_prk` constructor, then use the resulting `Hkdf` object as usual. ```rust let prk = hex!(" 077709362c2e32df0ddc3f0dc47bba63 90b6c73bb50f9c3122ec844ad7c2b3e5 "); let hk = Hkdf::::from_prk(&prk).expect("PRK should be large enough"); let mut okm = [0u8; 42]; hk.expand(&info, &mut okm) .expect("42 is a valid length for Sha256 to output"); let expected = hex!(" 3cb25f25faacd57a90434f64d0362f2a 2d2d0a90cf1a5a4c5db02d56ecc4c5bf 34007208d5b887185865 "); assert_eq!(okm, expected); ``` [//]: # (badges) [crate-image]: https://img.shields.io/crates/v/hkdf.svg [crate-link]: https://crates.io/crates/hkdf [docs-image]: https://docs.rs/hkdf/badge.svg [docs-link]: https://docs.rs/hkdf/ [license-image]: https://img.shields.io/badge/license-Apache2.0/MIT-blue.svg [rustc-image]: https://img.shields.io/badge/rustc-1.41+-blue.svg [chat-image]: https://img.shields.io/badge/zulip-join_chat-blue.svg [chat-link]: https://rustcrypto.zulipchat.com/#narrow/stream/260043-KDFs [build-image]: https://github.com/RustCrypto/KDFs/workflows/hkdf/badge.svg?branch=master&event=push [build-link]: https://github.com/RustCrypto/KDFs/actions?query=workflow:hkdf hkdf-0.12.4/benches/mod.rs000064400000000000000000000012261046102023000133610ustar 00000000000000#![feature(test)] extern crate test; use test::Bencher; type HkdfSha256 = hkdf::Hkdf; #[bench] fn hkdf_sha256_10(b: &mut Bencher) { let mut okm = vec![0u8; 10]; b.iter(|| HkdfSha256::new(Some(&[]), &[]).expand(&[], &mut okm)); b.bytes = okm.len() as u64; } #[bench] fn hkdf_sha256_1024(b: &mut Bencher) { let mut okm = vec![0u8; 1024]; b.iter(|| HkdfSha256::new(Some(&[]), &[]).expand(&[], &mut okm)); b.bytes = okm.len() as u64; } #[bench] fn hkdf_sha256_8000(b: &mut Bencher) { let mut okm = vec![0u8; 8000]; b.iter(|| HkdfSha256::new(Some(&[]), &[]).expand(&[], &mut okm)); b.bytes = okm.len() as u64; } hkdf-0.12.4/src/errors.rs000064400000000000000000000016011046102023000132730ustar 00000000000000use core::fmt; /// Error that is returned when supplied pseudorandom key (PRK) is not long enough. #[derive(Copy, Clone, Debug)] pub struct InvalidPrkLength; impl fmt::Display for InvalidPrkLength { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { f.write_str("invalid pseudorandom key length, too short") } } #[cfg(feature = "std")] #[cfg_attr(docsrs, doc(cfg(feature = "std")))] impl ::std::error::Error for InvalidPrkLength {} /// Structure for InvalidLength, used for output error handling. #[derive(Copy, Clone, Debug)] pub struct InvalidLength; impl fmt::Display for InvalidLength { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { f.write_str("invalid number of blocks, too large output") } } #[cfg(feature = "std")] #[cfg_attr(docsrs, doc(cfg(feature = "std")))] impl ::std::error::Error for InvalidLength {} hkdf-0.12.4/src/lib.rs000064400000000000000000000221201046102023000125240ustar 00000000000000//! An implementation of HKDF, the [HMAC-based Extract-and-Expand Key Derivation Function][1]. //! //! # Usage //! //! The most common way to use HKDF is as follows: you provide the Initial Key //! Material (IKM) and an optional salt, then you expand it (perhaps multiple times) //! into some Output Key Material (OKM) bound to an "info" context string. //! //! There are two usage options for the salt: //! //! - [`None`] or static for domain separation in a private setting //! - guaranteed to be uniformly-distributed and unique in a public setting //! //! Other non fitting data should be added to the `IKM` or `info`. //! //! ```rust //! use sha2::Sha256; //! use hkdf::Hkdf; //! use hex_literal::hex; //! //! let ikm = hex!("0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b"); //! let salt = hex!("000102030405060708090a0b0c"); //! let info = hex!("f0f1f2f3f4f5f6f7f8f9"); //! //! let hk = Hkdf::::new(Some(&salt[..]), &ikm); //! let mut okm = [0u8; 42]; //! hk.expand(&info, &mut okm) //! .expect("42 is a valid length for Sha256 to output"); //! //! let expected = hex!(" //! 3cb25f25faacd57a90434f64d0362f2a //! 2d2d0a90cf1a5a4c5db02d56ecc4c5bf //! 34007208d5b887185865 //! "); //! assert_eq!(okm[..], expected[..]); //! ``` //! //! Normally the PRK (Pseudo-Random Key) remains hidden within the HKDF //! object, but if you need to access it, use [`Hkdf::extract`] instead of //! [`Hkdf::new`]. //! //! ```rust //! # use sha2::Sha256; //! # use hkdf::Hkdf; //! # use hex_literal::hex; //! # let ikm = hex!("0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b"); //! # let salt = hex!("000102030405060708090a0b0c"); //! //! let (prk, hk) = Hkdf::::extract(Some(&salt[..]), &ikm); //! let expected = hex!(" //! 077709362c2e32df0ddc3f0dc47bba63 //! 90b6c73bb50f9c3122ec844ad7c2b3e5 //! "); //! assert_eq!(prk[..], expected[..]); //! ``` //! //! If you already have a strong key to work from (uniformly-distributed and //! long enough), you can save a tiny amount of time by skipping the extract //! step. In this case, you pass a Pseudo-Random Key (PRK) into the //! [`Hkdf::from_prk`] constructor, then use the resulting [`Hkdf`] object //! as usual. //! //! ```rust //! # use sha2::Sha256; //! # use hkdf::Hkdf; //! # use hex_literal::hex; //! # let salt = hex!("000102030405060708090a0b0c"); //! # let info = hex!("f0f1f2f3f4f5f6f7f8f9"); //! let prk = hex!(" //! 077709362c2e32df0ddc3f0dc47bba63 //! 90b6c73bb50f9c3122ec844ad7c2b3e5 //! "); //! //! let hk = Hkdf::::from_prk(&prk).expect("PRK should be large enough"); //! let mut okm = [0u8; 42]; //! hk.expand(&info, &mut okm) //! .expect("42 is a valid length for Sha256 to output"); //! //! let expected = hex!(" //! 3cb25f25faacd57a90434f64d0362f2a //! 2d2d0a90cf1a5a4c5db02d56ecc4c5bf //! 34007208d5b887185865 //! "); //! assert_eq!(okm[..], expected[..]); //! ``` //! //! [1]: https://tools.ietf.org/html/rfc5869 #![no_std] #![doc( html_logo_url = "https://raw.githubusercontent.com/RustCrypto/media/6ee8e381/logo.svg", html_favicon_url = "https://raw.githubusercontent.com/RustCrypto/media/6ee8e381/logo.svg" )] #![cfg_attr(docsrs, feature(doc_cfg))] #![forbid(unsafe_code)] #![warn(missing_docs, rust_2018_idioms)] #[cfg(feature = "std")] extern crate std; pub use hmac; use core::fmt; use core::marker::PhantomData; use hmac::digest::{ crypto_common::AlgorithmName, generic_array::typenum::Unsigned, Output, OutputSizeUser, }; use hmac::{Hmac, SimpleHmac}; mod errors; mod sealed; pub use errors::{InvalidLength, InvalidPrkLength}; /// [`HkdfExtract`] variant which uses [`SimpleHmac`] for underlying HMAC /// implementation. pub type SimpleHkdfExtract = HkdfExtract>; /// [`Hkdf`] variant which uses [`SimpleHmac`] for underlying HMAC /// implementation. pub type SimpleHkdf = Hkdf>; /// Structure representing the streaming context of an HKDF-Extract operation /// ```rust /// # use hkdf::{Hkdf, HkdfExtract}; /// # use sha2::Sha256; /// let mut extract_ctx = HkdfExtract::::new(Some(b"mysalt")); /// extract_ctx.input_ikm(b"hello"); /// extract_ctx.input_ikm(b" world"); /// let (streamed_res, _) = extract_ctx.finalize(); /// /// let (oneshot_res, _) = Hkdf::::extract(Some(b"mysalt"), b"hello world"); /// assert_eq!(streamed_res, oneshot_res); /// ``` #[derive(Clone)] pub struct HkdfExtract> where H: OutputSizeUser, I: HmacImpl, { hmac: I, _pd: PhantomData, } impl HkdfExtract where H: OutputSizeUser, I: HmacImpl, { /// Initiates the HKDF-Extract context with the given optional salt pub fn new(salt: Option<&[u8]>) -> Self { let default_salt = Output::::default(); let salt = salt.unwrap_or(&default_salt); Self { hmac: I::new_from_slice(salt), _pd: PhantomData, } } /// Feeds in additional input key material to the HKDF-Extract context pub fn input_ikm(&mut self, ikm: &[u8]) { self.hmac.update(ikm); } /// Completes the HKDF-Extract operation, returning both the generated pseudorandom key and /// `Hkdf` struct for expanding. pub fn finalize(self) -> (Output, Hkdf) { let prk = self.hmac.finalize(); let hkdf = Hkdf::from_prk(&prk).expect("PRK size is correct"); (prk, hkdf) } } impl fmt::Debug for HkdfExtract where H: OutputSizeUser, I: HmacImpl, I::Core: AlgorithmName, { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.write_str("HkdfExtract<")?; ::write_alg_name(f)?; f.write_str("> { ... }") } } /// Structure representing the HKDF, capable of HKDF-Expand and HKDF-Extract operations. /// Recommendations for the correct usage of the parameters can be found in the /// [crate root](index.html#usage). #[derive(Clone)] pub struct Hkdf = Hmac> { hmac: I::Core, _pd: PhantomData, } impl> Hkdf { /// Convenience method for [`extract`][Hkdf::extract] when the generated /// pseudorandom key can be ignored and only HKDF-Expand operation is needed. This is the most /// common constructor. pub fn new(salt: Option<&[u8]>, ikm: &[u8]) -> Self { let (_, hkdf) = Self::extract(salt, ikm); hkdf } /// Create `Hkdf` from an already cryptographically strong pseudorandom key /// as per section 3.3 from RFC5869. pub fn from_prk(prk: &[u8]) -> Result { // section 2.3 specifies that prk must be "at least HashLen octets" if prk.len() < ::OutputSize::to_usize() { return Err(InvalidPrkLength); } Ok(Self { hmac: I::new_core(prk), _pd: PhantomData, }) } /// The RFC5869 HKDF-Extract operation returning both the generated /// pseudorandom key and `Hkdf` struct for expanding. pub fn extract(salt: Option<&[u8]>, ikm: &[u8]) -> (Output, Self) { let mut extract_ctx = HkdfExtract::new(salt); extract_ctx.input_ikm(ikm); extract_ctx.finalize() } /// The RFC5869 HKDF-Expand operation. This is equivalent to calling /// [`expand`][Hkdf::extract] with the `info` argument set equal to the /// concatenation of all the elements of `info_components`. pub fn expand_multi_info( &self, info_components: &[&[u8]], okm: &mut [u8], ) -> Result<(), InvalidLength> { let mut prev: Option> = None; let chunk_len = ::OutputSize::USIZE; if okm.len() > chunk_len * 255 { return Err(InvalidLength); } for (block_n, block) in okm.chunks_mut(chunk_len).enumerate() { let mut hmac = I::from_core(&self.hmac); if let Some(ref prev) = prev { hmac.update(prev) }; // Feed in the info components in sequence. This is equivalent to feeding in the // concatenation of all the info components for info in info_components { hmac.update(info); } hmac.update(&[block_n as u8 + 1]); let output = hmac.finalize(); let block_len = block.len(); block.copy_from_slice(&output[..block_len]); prev = Some(output); } Ok(()) } /// The RFC5869 HKDF-Expand operation /// /// If you don't have any `info` to pass, use an empty slice. pub fn expand(&self, info: &[u8], okm: &mut [u8]) -> Result<(), InvalidLength> { self.expand_multi_info(&[info], okm) } } impl fmt::Debug for Hkdf where H: OutputSizeUser, I: HmacImpl, I::Core: AlgorithmName, { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.write_str("Hkdf<")?; ::write_alg_name(f)?; f.write_str("> { ... }") } } /// Sealed trait implemented for [`Hmac`] and [`SimpleHmac`]. pub trait HmacImpl: sealed::Sealed {} impl> HmacImpl for T {} hkdf-0.12.4/src/sealed.rs000064400000000000000000000051011046102023000132130ustar 00000000000000use hmac::digest::{ block_buffer::Eager, core_api::{ BlockSizeUser, BufferKindUser, CoreProxy, CoreWrapper, FixedOutputCore, OutputSizeUser, UpdateCore, }, generic_array::typenum::{IsLess, Le, NonZero, U256}, Digest, FixedOutput, HashMarker, KeyInit, Output, Update, }; use hmac::{Hmac, HmacCore, SimpleHmac}; pub trait Sealed { type Core: Clone; fn new_from_slice(key: &[u8]) -> Self; fn new_core(key: &[u8]) -> Self::Core; fn from_core(core: &Self::Core) -> Self; fn update(&mut self, data: &[u8]); fn finalize(self) -> Output; } impl Sealed for Hmac where H: CoreProxy + OutputSizeUser, H::Core: HashMarker + UpdateCore + FixedOutputCore + BufferKindUser + Default + Clone, ::BlockSize: IsLess, Le<::BlockSize, U256>: NonZero, { type Core = HmacCore; #[inline(always)] fn new_from_slice(key: &[u8]) -> Self { KeyInit::new_from_slice(key).expect("HMAC can take a key of any size") } #[inline(always)] fn new_core(key: &[u8]) -> Self::Core { HmacCore::new_from_slice(key).expect("HMAC can take a key of any size") } #[inline(always)] fn from_core(core: &Self::Core) -> Self { CoreWrapper::from_core(core.clone()) } #[inline(always)] fn update(&mut self, data: &[u8]) { Update::update(self, data); } #[inline(always)] fn finalize(self) -> Output { // Output and Output are always equal to each other, // but we can not prove it at type level Output::::clone_from_slice(&self.finalize_fixed()) } } impl Sealed for SimpleHmac { type Core = Self; #[inline(always)] fn new_from_slice(key: &[u8]) -> Self { KeyInit::new_from_slice(key).expect("HMAC can take a key of any size") } #[inline(always)] fn new_core(key: &[u8]) -> Self::Core { KeyInit::new_from_slice(key).expect("HMAC can take a key of any size") } #[inline(always)] fn from_core(core: &Self::Core) -> Self { core.clone() } #[inline(always)] fn update(&mut self, data: &[u8]) { Update::update(self, data); } #[inline(always)] fn finalize(self) -> Output { // Output and Output are always equal to each other, // but we can not prove it at type level Output::::clone_from_slice(&self.finalize_fixed()) } } hkdf-0.12.4/tests/data/wycheproof-sha1.blb000064400000000000000000000563351046102023000164130ustar 00000000000000/0wA!MP+bJ[Y¾U➩ %YC[;Z [.TG% Z>a1>%P{ +Tˢ/ +2-`ὦa`[-1hTQ7wR6 ޢ_Jb/z"Ei&<9 sBm "cgSQ7>/<8Kg8Ѵ *)"vܯ&Ow.B} y=Zbݳ@N)o|B!^ 둜8+Hߐ9GXv)J n"#7`}^k55cuJZ#_@ f~c WYBSZ&<~齒 XG2!\@/5dU܊ } H0SC3*#HxÃh  TZi3V省K/[ hU.B$x   !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNO `abcdefghijklmnopqrstuvwxyz{|}~ $ pM`,Ց*jܮ0Vs/y4j{qac檭_I;Jogp }kP3MӥE^3  yRVZKv1<03ݮf |5elmvV(DAONdn^0wIt(g0]a=ҞUo I\o*!~C]0<rKCמp[3zO3(IT%r.+~xhs^Zh8rZ(ap5ҥ΍ ,`ts&rsI c=/Ӄ +H {[J+hBmCwy>$nK=*Vf&+.sb=ARݿڏzen rŭӈ峼$TGHCܞ BZ &-'UQnmxf\ 4XgjtD z2_6>3x YeL[X2pJlҠ:,tE3Mi x¿쏫(<.)oqb`CX2ka1ޮ(k%/6y ` nC]3S1j⚨m3-)PQjoiTCjB1 vM V#hije ĄjZlSvv({x}CklnM!*!R}.2b;d嚔/ChqXW Tg p52YT>̈́I3Qp?vi4m+2b{}tk@3.ھY+xraw8颙,W83y0@~vH9/1PP lNWk-#<7G7I diJľÆJlc:ʇqO2JJ Amb7ꈉc J=2fKIg&t;f6r*+'$9/fn9yɂeAEvݪIMUfջ ;{wRԥ?SKc.ܦ䶞lK;?&Lfq,w\$U'I\şLFnmchZˇ}K"W%%a֗jRs拚ĺOuGB%DeVBJbDxM58Gʬi,!/U6*UqGb Pb^`[0 !vv 3ѦoBFPtgR󦽦Ve0%BZBb8FLOák )rKI7 eY}SzЀ2񒩷M@@ȼP粺۬Ը Ӗ/~( 9=l.AN-QAHѝ}郹D T3m-Eñ\HZzzo#Fbe^m232!m3Tg!,r]1 rDLoPJԂ1Y2xa*.pϠ>ZTU8'wQX&m 6%ߑ_{FtCE(?AbDYf|7k)S sh&#ÑhlAB^Zg%z?h֮8\}xɭ%C%-nf,$ M8L2پ˱U]Hmi܌Jx!̘ *GJ?Äkȫ,H(z{%Bݞf oNnv37cē*cv|:E@R#*λyZ+:%Ϥ^+ٖٞl$^$>d_ޥK50Fx[)JP4ch2㍃KVԖQ6gc옵7w֣rY֏fN> Ȁ));~N0AZQ>JI؁Ó WB``@BL'L o=`Rɒ~U/@0 H k\B ؈w%D4*[ {7ThQbnO\u\Q́ܓkkJD^-Gu>lO]M HɇwHk>i4-Sfۛ6uHv2}w27*%dw1Lpsi[k+[ߊ3= '6+Ye}U֔/JY*0en69l[ @\\݈道/!*s{I;whI18ziֶȋv@DhT,m"L?[J 5G%Hd+ȱ,8;+dVS|N( ZyB9BvcuSme;O-.1gR"_m [7~ *~[ܿYtT㑲bni C3K: j&C=?kRp]jFx Bb>ХhȒ`Ɔ#Q:\;[AM&$Hsn6fQ.UG܌Bbë/)c'lgĀNgg-eZ5 8"r_JyOopţEBnÉ ~ݮKb"ϭvxҦZDn԰@4;v aV7S8K~x:뱰Q#oMgLrAn*dЪ+n-mYn&x:WO`f+EQqcd=GPzK{H 3D oytϙs'V|~uQ6Qݑ$I8X߳;kHXvQKX{^#G@+*6&`J?ߑ3Db)k9}_mER$rӍLi%Mtr,I %09 6(S!bc~5Y$leeeib=8s-9ի)df _Yʒ% iBBS2=+ MRP^~jM 2_5u'}4 2q8O-<c+].Y%)-AW pfЪKsI+wzv1σT+.9BG$>oMMYtk(q'6Xªڥ\w(9WV"rF>CjuCx_ҁ=\s[#@qT2cxFomoڎPf4Z~d~4t^@zڟPhݤR1.)& _Ә*D4wtESϢ$/>k6Cx攇y:9)iD" lɸRcP6/V}/8!盯D]$(9bi%'x$7|u Gqژ Lj9qRY  a8:c70 R4y7^T  R* bjnGlR̨Tм",Cd2y!@ɁO_X~+ /S򷺸fBzG$&w~6wT7AT^Ffpw&}s1ѐbMÃ<1HLсh@w!B=YSш؄%֔Bye0 wX񎀮9::z e-=8_ Ap Kis aIlr$ c+q&Q߮=7{ * <8hywZ^*;x*CH놘laGC=ŠP 3c*nfJߍg !%Iv vKNb'ϥx ɕ<)xblDx3du \jM<# ΆoUYvu'1^6 խ`RfGG^qT:E~?C7Qؠ0m3눆g[/wB^wYO]osffX;F!?3cS _rc1#FX 9}ц/֥a3Gl\c[K9#•e~"E" ]T-U$qd,ic%Z)% IGU'@OͱZF@-rm_tI#Y&Hz%Q]9sT?<_AqXK:.ItF5A~qQs2}\QsdIYI^&D0PDg3ig0DjDѩmGSzcR7'I]P  SPk7A~ .*3A W8K Iml@M pO 7ũl~2|yHnЈwv@[@R|ʙ?4ꢧ~#s>նaQ, JP?y}*/ʜ&?YGCbLTPs>c&h[2mÔ5^^F"jJ!yn:.C6J`@ ߵW?slC㗽U8wor-y@IIS '\a J@(B|}ZBEt_BmA}|ϤYtKY R& rUS&([ỗ3[J ( *nAgc(1LZ ňNSw\(Xz:Yzܴk%%fyܿ {( 4 EHTD./a(D%X<)0 %zBw+SS%&:k1a}ϡK;(NOc)g[gfB(ܞHhM fX }סzD"Zb(eX_|}|x(4\4˭rMTp f*Φd5 Hd"_T/d9=P ZàĖRv9ԅU,^'(Dd d/{`Z1f i@U:Y֭o;WH BqũZ˛ m]t7tld}FIP1Em( ·>sI2 y2z&phwC(m#=&z/ZfQm(iaJ.A0V#6n(fg!E g0 vY]^(E!}X(mDTjnv2l76 {0`e>g5d n/(q\ٙ$<2p .0+MӤ("z\T5m{c!ހ2.uTj ,@6:hk_g8 i_h dܹ4ZW9(kZy;Q3A<&=f:'^.RBy( E8\3UjP3o( ȋ)@uN',Go{0ƖO:ڻL1p^ߦ&fP;iJRÌpbLj]Zʹ( ` 6 K}M<*ݳ&9ѐfz0=Tq,uV;(<XyG^T1ϵM<[+pJ@Î.o,B E(2^fH#f{+<bÓOVx']-Y_ E(o:` M*&99I~We%7p  3Jp7bAk5~O]qqQ}7pSEZ:,(!GFi&6@'Ϥ ZwՆgm"UCWy\*'U{O4s,LEM+YعT79E>Vg A/kNջPliސp+( x;fm++j)K k 1m~+B=*r6l]/Drdյ0tNHs^JIֱIz. ( jd=t4y T*>q˯|a#L'?ӃtT/(q?F\(Gi/ڧs)=ErzOJKh#M(Ϸ$jE w!(V5Ń+ (pKcg3i\]@cb3o"JR&ޯ$FK(+a7qA=?a (Qr-햗Tfʓ# .8́os"fŤ C.¼-͐<[x룩v|#Js$U[[`sq(ty߳iL.}>(¨.Re7'JRzg}{Xu-_ͫx!sC^vi(>qҀĖpcJGwTxٴf+1K2bX3O -+zS0Ne%ǻu0S݊rӯKY=TK;=H(rWޅdF 9ոepMjM1?Q_cQhA!O_Mzy3z;AzygHd"Y3N՜{;' k۷|<gȤcIӹyL8] 7 -^ KN hc Iľ YaT2gӚ/ey,;$Qt®^c0r%#5|9ekCR@Xy ~n/_ .pWhAh"OZ5H8ebҀ'.,<йb~?՚;Fe;LIn!MGa2­> K1,kSf*tnUaݖsyQM=d礜kpni&>CKO>`f֗$PdD`_ [ڮv-x;H1 /g|a[Ѱ81;i|\QSFQzj 2z?pw[`_` U5H9ZiH]QD]3ʯ)3c yp,{(}mO2/"#lԡ]z) ~W`R9t(jO_omTE f0Ke5Q[7ƧZ5*N%m{yl2kS=yiptO6?y2t[ʥPPs4d6z8oZ>/ў!aO0/gT0ט5S /qu[zWKe]Z3=kFgE5b4'ԥ# o 1< [.J+%#S?c$jZb̭,&.1O#ݚ} `i}.mG &55{?8iз~QHLSO1KHկ1'u3k9ijC!6ECD:.+5;& k!1Vm mYjFVt 2&^"G eԎvPâ:V:Ff-z *4@WO.EWT{KFBϋB\#biU;dFLBQ i} Z*E}8/9V'0ǧ=G{kSY\Ȋܮyu tϏ6'?xԲnBu(R܋lS'? r ^yw_k }xPXX fNK4S_ ذ(fq-\j0QKgsjk/MIaOWrLggrv|ͧ:(Nh5ڋ  $._v9-p&QTMu"TI5G;pFP?jy~^afE}l3 ỺGGud_@?9fyΨB\m^h\.~uxg Dx|6Шɼ\2F^!"}GUшyY ;/M+`O{*"Wtm>}3Cz<, ĖeX,O`ܣçTbq R痰>M6ćo{ Gpg1&z M:_ 1/3M*J,7?g8͟yJI!ED7KԦ9F 1~64<"pV8 /]#E~%[):;ߥtQdDkm2Ś鯹 \Bx(?4gkVr[wFÃKØZ4p ,G Zr"TMgF{&oHt#IZ2;8 )f$^)_EW"9$;H},+ ux7]O{Cˮ`C7Yn2NaJmO_ J߆6f3_E=VcrF *moq,m͹%v^VHqÇC!SH-Y.aqZ*xOtxj_D-Y5pٛ[C- o)/bb%DmZ\RI (g>a5XHm&}eQ*Ћ6juJt|p|SzOU'J2r7p09Xg1:ApK(UlxB"~cA&!egUO{G/g*20T>; bb0Zy7ql_ @# ucR S \gL,JxrqQd <1m  eZ,Rɭl"_dDOo4_c?<:VZ22c׮m~rt}p'UIdsb?4#6v=۪C+]NKkյ65N$^ݐT= X`Q0?I9 H1 Em.+L%J\^>9` _V"(tb QH Z<}f\**r"xnKNaiSu,n /3!KT Yo%-S`[΁ Cvh8,TS3N:Gqn'ZȢjp'FxU>\,7p3PO@0:ptvY,:ݍad<8)eF3N %-\ZQsVPWv QViCru,yITjj [\ oȈXa;+{O&a)_?~x ͹e"Cz"jxE>o|F!mth(h3` usص{_V*]X N^j06n(k! 5,y8;5F3Bdv& 0,N+({@˨8[RT \a;a-c wYSsCUhH+C`!+=PU={zFj}|l:8x|HғRz@٬"dA"d& QaD[|WO`Ŕ 7kyHq~՟Ş^>'Gm奤}E09p:ӕ$Uuw6q_a?kްJf{JRLob\ Zs)?~!=:pKʏ(>(hO 5쌪W.bo"WLޮEk "'Ģp|V֭kϋ"ٿixgaS^>qY;:Gʏz42p "1~(wY4$2dYQgݲ&NrhW#VN\G\yCf6c-9zPH +Bns'&;PaiY`sq I^;5uT74 ZҏE:Ea"f'NJ<-lw+= 1HsmiC)s$vd@$ݹ^6"LgF=,* a<z^HzE-.z,Y vHmNb<|{$ c1bEޡ8a8d TycS>=~r :u2ʸM6VS[@ˤ-*]-VZ7iHS!9hz'"ۙmD !r5 6\X_}U3][ft.X!|#;EuH{@s/Dz[s zDaCk(t0Z$A7,{؀m>!(+M:MZX< طF@iHR4p~>D WYHg>rr8m(~ko~ g:PTb> QHa;J +N3d 6ۢiʺ<6cH@+8T;HIzbU,k,rj Ʃ(;pu";6)i;PBz䩅4J u.\o*B4O=`|.<0|`H(ltI%7(t>8o@ kOA'ֽpzas4þ ڧ½ GDG!yTJ*(Bf5As'TuS˺beTdBDs7QU_5k_ȷ3Mj@ӖƟMȭEVw_J"~)<i3u4$m'I8~},bfco%0qLX. 6[99hw4.&# {2R{7@= .N*D;3Fyn<- <ˌɉ JËp!v?.([@^w(i$*R#ZK 덾=\w@dzK|}L6lZ(U^ .?XAfg(6woUr/q7hsnɉ2r"FxC)6zUCZ#ZRxkhTkEGO)A@c%JC,ٓ2(76+ oU16HzɎȴZ^wFF&QAfmK^CKm~c0_iT$וwމZ6χ$$>,3g@[ME 9/l.e,#l6ݠ +&ي"%^M#Cʿ7b3g$L;XX9(a>51b/GTOT|łrZp<,\S?)X㈌]7@0F@Z%&E*k~UVXa;A. oQh:(\Qric YK70*a\ ep}naVp+*F}iw k6_mo~֦Cr: byxZ#Ȩ; h-g`{UGl<^jIӖ+ȫOSXc) X d*suL,~cz([Qy(Ћ>:1kV[w.QYa œK޲ƌf-lQ)\՟ȯtP>ondwtf^^oJkgȟWf.:Th)K 5 )<4uEoK\ cG g(670G*{Lۯ`ۈK IກMo ͑B"X)0^ճtRd 'tolad#+5W޸-*젘R>O!Vwtd E;+ #l+qs6#E/ ^p=&t"@ IЩ5:Kql'ۈhVKCSYss0y"jWGZX\b cPѤO$;U:%hz~EDNR޶)41Ɓ9lot:ve-lJ(;򏵣+B>OXD`+Fי9N: 0U7eď̝j Y|`` j*?K< bRUQ0X[&k#؅ɍ"w#p"QSA&@4*Q%~CKa;PԞ-" K-Y%(D:Ur+?ReI=Wq=*{K)q v%i>E#õ*a HJK;%bIЏcnܷ5GeMrt:˔^@SvQ̑^vRfQsLl_.`/5{Y-K,%*ى~CžQ"^Ww28=tYML$Qaxn1Q7uD1v})S4'#}cz?+L CU=m*:Af; TyHXAQvcu*WФ~*M _`#)q\48D\K/W@.4G(]}`{qФyRC}W\WI/$FwӣX2j-LEsrGY^ sPԤ1]/*ZVA \]3 Xm;@i6͎n-2z*lNb{LU4=\'@Ϭ@%JB`#%jOϹxY5-E[>JL"=wBpTRI!9{mhfM4a/q!`x45nȈAuzμ2V&B{RCœDQyX0@az`$uw6y>l3 Tj<03‹СX,<ƉN)qUO9Oy\PΆ5 v'·;w3Qu\s$*VF ݝL`p'S8_gaB)<"^W:̑{hfE0weUSNp FGXU&%UU~.VB3؇ {gg]uqayKr2k0J¥LX!e+0tܻUxER(S! }l@{7Uw;pCo+}D21v,:֍)ctEܩIALc/_"suت2w#e͎R5lLx}ػzbyAh8 EA!I`I(ǧ".PP]Yl[~ĜVQc7:mxVqo\t | |TO+!5TUefXME0)1v].k gLgm֤\ir'#o(yۊw3X%[Vr1nKAf105jD݀ini?|Ӟa6R:v5r+{qPoP0*5GdnPN> еroDlT=۰H_ѫ8}w;nbI[~q߸ bBD6w.cV$SƉ\Fm_<MIn4Ufz}8⤘-4%o=I?lGC(A{U=a_ sDð6u [z]!|-]5mF٤XI,M$\xzZޜ cb0v(`:G^2gtGh3e)څ]ҹ)Xӑ,쇽-PoB=@nET+z:pL:,d86, e    QGR2ISL~|1ףh>-3[a1>%P{ +Tˢ/ +2-`ὦa`[-1hTQ7wR6 d zc-K\u=Gt/$ Y@҂W]#mVWt/MW Cq5@azpEGQ%dF/k' f .2ڽuz_7 QB-s \9 &K_.ˑuȃZ7 L ߩLųd{OQZ - d*9+5F_-J #K7UTCbםgژN!nV ?@ABCDEFGHIJKLMNO `abcdefghijklmnopqrstuvwxyz{|}~ $9'YjI4O.-NؠPL|YZǂrqA^Y 2u` / 6wq0Ły>LCO `ESF u(] ӷT;/ v"xҭUXT ~ymR`חɖsD!2J ܹ*vWȺ QurwΪȂxKQ&%t!{U) S ׊ S; -CKL0J(t;}\~8%H`D(=6o)VNP2\|)+m( @UShk( v nu]KTsjlwD<ރgKFՠrQ [1f֍`(hϧ{j~Hy$:zW} w-BN#ȸ_J+P[W'%%Ip$ͪ[j Ft5s~ As6XVU(KOTa^16KOh 3RBӜ` Wp%Bq:RT<@?^qZQ*F]cZdS|-* ~v1&Vb;8s LD>߮PGc,53(՞=e*%/,?)B75&RQJzn#Q.}[e/i)e> \A֝$$ AN6bs +t3~C'(? ScirI(|iq|O ]=8 bW o;8ԯ(+2is[FT'L!6{SRo>.fvۈF"]Xxso;3 wy#>HwO qD`_Mz(cMxKB °=œO[FtKibuA>aVc(|pP%)$p}THP) `'yC%;=1<܅Iiګẃ̹_'oF沾'(#KτOWä㧤\ W7WL\zvF# ]0<rKCמp[3zO3(IT%r.+~xhs^ &ً<WtI`l-<.x@nLM§CDITbѱ=@1 {[J+hBmCwy>$nK=*Vf&+.sb=ARݿڏzen rŭӈ峼$T1qe;I`I7Gl'*U*B:5c{2ٖ 4XgjtD z2_6>3x Ye3z|8Lzb-[0#ކߧ!ž7:^b/,Lnf錉!#"4pN5~SC|I΅;n I^op>[gϔ9SYTf6ooU@`e!n0HEם#&HdzL;S"TZ7AH=ҋTrr(qMZl%ꃍ:Cv@s6!X VʇҎD>V?!Uu>坿Q؜Ը\; Àt*<瑿Wm NhnJZ6=. ޽wK2Zj!32|=; *XGZW 9Y 3m>e?(u pR{8^@UO$RY8qV7a5Zr.([GtP_n^0fypƌ\!+XnRJlb-/"ƻ&i;) &0߲/@N (O]x-ͼ$$.jL~e&ZI&rx6!)N+t?%Ï1} pk@ ez&}ӼRs1}}̗9r3Ȉa#bkUTGc K*uoGYyO!(-o Fpc~z\z2ťݎ)u:E!4l)[a1iYw~,B| w70H~]'P Ǟ]'U!. 1b~+@跀w8軨L2i*"8wY/hq}f>OwrujL"?) _EHɦ~>ku9{EPjȉ[!qUU[X:[@b3n#'%V1?=@Jɇ'?%p069)-kU;OI yjs8b"$- $N1,t$ը̧+[fU"P:~L`Iɿ MQ>6lo,S4PAb)|DH>*!ۑ8 krY쨟xT܄cb PYeA~ "!Z<.o vUE~*`9:g4JŒQg]ɵ& k%@IoF\>云{bL5S_"9p~5 W|uqn%j{vl0x/ˌsMXwOSp\5L@k H_f8ә@:p߄S f8\.S2B,voA璴W$Je=}<vufcx3G{qgu&.&>3HP^AfT )sc1X*n[9%㕋OVn#?!䬵:H;D>㭁4J_h- )zgC;AaSXЯ8u2WL]a:w2Wiymcȃ;OiXBV^ X&+SnO/ȍdkzwlk| 4S\S$r@PB y,%AVeIzt=oHOVxX ?|f{kidp_s('.N4-I\ ZƊҀ}|csc)u[>pӊw` /EpR(vj%["H[Uu,! 2[t4^Ut(v}O+^"`z<1v{#2Vɩ[;-3 5U?¥u5EfD*-oiWQGo-9=! VhTow'OlСk61!-oC?}Nğ(_)>` {`ژuC.n7}cަvaӧ<.IqB8䪌Bx-q؜,C3rb__{BBÓբc0S  i(KaBwa׏ r%p"'a6_i/HyնJҏăeL,i`Jy,Kж݆QbYhA=M.*!VnL:pM$krSR;3eNƠ-Iqu7t3uy%ˎe>R6_VA ]D{ XprARr -1BQEv^#DzQrg>bhwo[ղ6@ 15Wп̈O{\wJ .K,2sKO}9(0o@Cf%̗B[ntUc 2[b.rWm[?.I53S6i#Fz+5YYU%䧒{3_6wx[ IpZ\GM3CYsa[7z _AuKdN mA̽c3?Ǡϒ '%U6/;6<@43f:ԧ@hx#ڸd-)peYE hBeӎHFEa}RY>Y cCW [(ֱ4,)ڥ޼ؘRC$ŴI1i.3nzTw&( \6Fz&Bsq1tb@!Q ƆXLnl߶UMwz;=:ѰtoEW! s0NƻDtQA ` #[::p&5nm3< 8i 0—COorzr=0)z.IYUeL_QEz:6ʒ Y5R8F9y~h902$`pW5/e7 rk&.d:`jG(?'9pY\Lc(vFR;vqR04$ϾDUZb=Z@!Z *R^sEe&!N MYjT0Ϡ6Xe7BTӦX/IJB!%Hf$eRܨ 8*vz& ?颃}yrxvg;a^)LHPj~Rza~D _Nk~5T#NS5,pA=C7u ˸7W>?_4|t_Vߋs9=5q Z~9yrvRrr[϶m_2I B^.W!7QM)K66!L,v!ÈvȪ0pcl)REq n$k<ٕ}j5gh3wRM9:Ra"#d>?&]KH D馿 "S6'wp#9ET@ihT#Jզ0Ořt QA6 ʱ0.W!!=>ݟ  RT*@qalζ>]=4%L(?zm3= lNC06|e?2۸|4jrr<D=m_1}.]ܑ%lY-رT៬'= P2ן p_A>43mRX S5@ dbw5cɽNrEkfH(P,[q܌R, sr~=uGJU)޵E%Q!>91&R8kW$m-%y-ˍnM$4:(ͫP=p? L|mNPmr0c&Eɂgz.Fj%ozټa̔GD rL%zWՐRl_M3QƆpt=,D*hJahZ*]E_ g7Q@y:M pզ|Peˉs -yϻ|pZH)k,122""a֜:Pw$d2"F6aEP!d|1RL?zȶiU1~xQG͍nЬ5;Oxmݗ$t֥rCp!uN$.ʚ\ֺAWcwYBC77Wv֓%E _7ݝs㲅ĔM ꏌ;|4/1 45P5Uo$nKFQP Rmuk.c ZPa^zjB{$}v$<@c'Pc%wpd~ ݃g YTG˕[ AB<'IrewiX].m^NRWto!KâO98$  ̳o9s0'-MYeNPZUV b\j(g: mm:Q5֭\s/ؐ4˲X FLtX_lTsDiYBPhZ|1>)]8L B@A%S6-- s~̾1ϲf u+ңdӥ ?w(IO[Kde {$@Ȫ|gGZ̧v!2 t @#V^@.h;I-[}{jy< y"3 MhFO|>PT)2.SFJȬN;NZu4]9&vcd.„ Xޞ})2D^}V& vgcOJZs!8Cj{4]z NˋA~#pQT@z<i̵ .KW&ʭU6 ʭU60ʭU6@ʭU6PʭU6`ʭU6pʭU6(]kjPgz<) Z(HK[Zi2#(zW 8V/t ͗T' [[O$~O+^{O+w&ljƹ]S(B|}ZBEt1%Jh|rhJ?2"Hȏ}C)UXEP;W;&S#4?kX !i*([ỗ3[J ( *nAgc(GE$8n-2t(Xz:YzsI2 y2z&phwC(m#=&z/ZfQm('3 --"Y5ɺ(fg!E g0 vY]^(E!}X(mDTɳJ\ȕD*6 m ]"4 e˦yu(q\ٙ$<2p .0+MӤ("z\T5m{c!ހ(#R>5lEB=4ʆT㋊(P.b {0ƖO:\ }.-ANܜ٩.@_w!N;pB("m×u=7YDx6X:f3@$)TS܅cl,nяy7bG]Uܟ5 Ss?B*yJSebQ)TI):"$g>wb3#,z஘,1_b2w7!3~Y^pl6$xZ삳-X%?b&%g=7{WluO&\.wKF~tA\S{Hp7Gրjv>:]Us]'91ȚQWM!`2qf~~0Ulp ,sg͍81h8ZQFJnQ #9*?x9N991eG|v29:|c ˝#`-*f@beJ ZG؟MCTJ)zY>Mac%RsD+Y7ttxQ*bRComк:JG );!jx=9fjiIa񵇐1VI[E?;ͨzK{j$9dRʜK`ҥ9Yo ڞVܥ`pV~ 3?5&N+Jw8~~h0!_H͉6evְk)DžI۫T_IS"(u6m2\j؋YˉK`~VI7͝Z(Wf蠧"{;3wa GPX}2C~bM%DX4x\ 0{x(Z)1oPI1C D|SF9zNi'Pu-Ab#e?۹^&@+{oSNN]0^X\5f}tΝ5& V:$\Q!m|$(ڢV%TVސH(&g>y$LZU.RA_q̀6+UCHC7JB]*kQ.Ԓf-{^mYǷ2i/xa'uI9\$O 8gy͗W`6ng`+9f4I0Q2խ QT0+ &7iV7=M[Y}h^5%x .g7׿KTJpkI noK4)̜W/@U2YI; eNoN"/ >5}}L9(+w|sID~CT~h 3[JX-LϤ*f6/ԕqkDP0Y 5T9q'NV܅5:'X`=:0ekh:'f3_WC ށDpYS$a%PXDFϏr>UEriԢq*ft`6Hg~nfցǦ)xm_J]λ oJ TYs9nwJ c@jNR#4Ѣ@ˀlS9TU {-{$N DdkƏݢhA5zvla/\ aoQ$"cChŷ/zM #gg4W.yzEmg/"|BT[NSx is6LB 2Ye4Nwis6А C,ُ@r?*J*yG(߶dF+>p5SU)xT9=@V˵Q0:ǎ,O|Pԣ3q$Re= IRƢOy1K88w $?XeTaKЅX/(!-U =̆xN,gd2 1 X+[EHV՞kI!Q҅5ݣ1taaޗ55S,& φئe1Vn#b9wtm Pm amM-<;Bai'Z%~i;4wج*žӶHؐN3H65쯯UƊLP>ߥ|/B}'DSc2d=1*uC>V9 N0F-rZ F^|lw/-|e\˲wW(P. EF#{ &G%x yցY ^ l4y^vk*gpK}.n b̢)HP/!rKmwϕ[JPg!j/57v:FZ~Ɗ|٤0Un:CPpgίdEGȍĵ#UWP Ш4J:zg錍. ՚`ĒPԋuK8F+@F )~梿 żo|U+T (tπ.˾J1IOPL!Wj*tBs;bGAw+ߧ^[^7Wj~PّtvœAg k76Y! dJKh MՂAiSMҏ>*eyyՁn @3 7}dGyjvGxЪNpæBGH|G,9n\Ȁ{z{{,5tO Ln.vާ^=\+B! oC9T4=N]x]g- }~^^4h͡ږi匭%Mv~*'#5?\@/@_ObMk' ]YB98p0ZgǾs#K >! I<Wggz1q*2T 2(5?D ~_7oV\C7S[ʿgͣj?S 3Ume/xV7#n7nfK_s;- 'ũ״ˣ~V|^e#{ ˉ~/OzuJe6g<و18` (smC3XMO*kauÄ ]c0m}w-@wө|=!ю96._Ty.8DR>#s d9?1si/T;y&K@~Avׄ>|ŗg~LNvNpOGA(HDŧZ]殁]!}Ԡ[sWk%#M(7Lp.%gb>$D/x xWM3blGg NP3i\%FfV+B3%OQ NŹF $wϣ(wπ*['0Ko%y_f9x0#s28 42w[*+ZF rƢJ*V٣KU8Eqnld}!>E>O-AufYe8i^57!բqR >ӥqǢGJ?Л^;na}?H{sk.jljDweW+̹A!*W:h-`懲 xӧzN .-{5Yv5;:Z}3ÝdHԶGTox(7 _, ~5Z|Xlj˱*ԼmRB'TyGE|> ®[ҎP>Є)DvH{A:*z%>t9=iB8E.#DMl`=w Hko[)7}}==Vr!*]2h,6t: ~1eS|6SI)2Z*wrSnV"s{F1LF/fxkDY8AU=vwwnZQ o$+w|Jsm GxgrQ1@i2@ J)W>Xy;NYp7?n6ȏnjQlҵÎ}?;;]A (kp3(cL)Ln4kh ]eYNBDܫұ@IY68Pj澬L߁~5 q8]A!JTu0%\qİhT#.eZ{k.~w0^In$)Hba6̒5Rq׎BN Łdms=ǣT5yyQOHٞ2G=2M&zɹ(@@'oRiqϯ ]A!>8\ig8^\H0;T^Φ>byoZBȎ4 =.byHoظ/䧃 JWvED̂LN3.(<4̊<>Z4皊fy_$.κ2&nU%cV6v̚a.uf$*'im` HUǧnqAy~ >/-_,l:2d k Qϐ+]NQdu D,oL$ 9Dੱf`I2vYdi/`>^i?Bhl* W΢uִocfqv;RYc6̱;{84pRWEc_2Kk|9XH`#3ABp,f9+" .)O3@\YS]4AyAZ>H ?K3U*6UCHnF=!` iflIHagkwU<>Vh5B߆A{*&<֡_p~cQ9E%w1v)BIĿv8zMz H-޶G/־F\&o@}HFXQ!`0`9/P2Ty )=Vhr}qI~/:YAO3ZR?RZErȴebǁ9k!f0 `} ͪQm 8q#R9'[pWH*y*4V++Y++: n%nLP !s/0m85v&) oɥ] fLnysE2xu{0muQϧڒUsBaX3jBd} @l2<-)rh !ERx i9/d7<ʕU7Dn',_aѭ$2$+"sh8Qg*g8%Lad!"J7 zjCk B1=.xK'ӿY=$zl⩼qtR٦J'2iډ{8#TfTOyS%*mg}9`-~1o,=SVs@8^%)%чJe`qK,O+YU1 vJ?Z!2s؟U?_yK=9+<U<G%Fݒ־>:KJ!w$4 dX ,ޜPz3As`M_}}Tx0HV~!}&DL 3q%ͣ)*FVؽRbIsɼN2L!Uw:w}a jՈy"umaR+SS@7'| Td=XMIr?[.m^\w m^\w0m^\w@m^\wPm^\w`m^\wpm^\w@5 c1bEޡ8a8d Td?67-Xk[-?9&%S?IJkO@ˤ-*]-VZ7iHS!9W{~eX X#֭"䰯p~H93'N^H=Hk??\0@s/Dz[s zDaCk(t0Z$A7,{؀m>!(~Dt:ˍbL+M@iHR4p~>D WYHg>rr8m(~ko~ g:PTFt go]^l5 67O=`|.<0|`H(|7緮( @ kOA'ֽpzas4þ ڧ½ GDG!yTJ*(Bf5As'T>|넳:%j`C"Rݻ $hm@'`z܏/( Ӣh]eϯs p9)7jn b%1(65l(|˹oP*FK&*Lv`1{uA*/Sf*zTUHJP_ "=C;Q@UD!-vӒ~ǗC'BE)y?<]@ 1X.+v ;"lb(3q;p:NL@? Zk=1;Yú,:xزO0O,<<lOhKiU+ >S˺beT}#+>ꏉ߲RzNKg\3]B?"IafmwA@ӖƟMȭEVw_J"~)<i3u4$m'I8~},b(X;76G-DE($61W*f l`R푳B}b·ve@= .N*D;3Fyn<- <ˌɉ JËp!v?.([@^w(1TAo\Iw8@dzK|}L6lZ(U^ .?XAfg(6woUr/q7hs2[G*VX5s ;VU+=hg+~x A9+rv @c%JC,ٓ2(76+ oU16HzɎȴZ^wFF&QAfmK^CKm~c0_iTimvxѫ?ƙh^.v6.Y}W@[ME 9/l.e,#l6ݠ +&ي"%^M#Cʿ7b3g$L;XX9(a>51b/GTOTFvy S+41qog>q@﮵g]Fl&CTe\'Gw@ rXAvV \)L|]!P 2U.fVnv@|F.Q/yd=.ɢ聫@)l@gЗ{}tEXݫ1]3Vd@vO3~<./k(ZeoFv^b{)_Idp|jQ iiV!dlY޾h"u$T1Ó6n_!_N@+OUY2xQ^tż2A@{(}yc }&v/"fHaiDeutwbjΪP-b<4wso06[*[(S{bg ıGi(L  Jtoe8;*i 7@Cݪf>R5vܾղ XA?@ _"YwGy$·/nݏ@>I=|KRSaЅ4{ސH>=7@_ٛj/𹧳+kܖ{xע!2T,Xv`dX#w'+<ɮfb[a>4`d>am\ky,]ܵz!؝qǽN$E?i3Wߍ##Iu+RaA| mP\_m |\GibT PtCLlBZ"3&=V w$ t'&Ko(iڌ'ԑU *ZڃhlCvÆtM3s\tpYoXtNn2iK _ȢZH/"Ź;wv= iܜ Rqp=Q}h'B:1}!"8$"IwXGdYֽ(|3:(n[~6'ec_g1xܭ3o%p͚yؘ ×.RyM !'^>#,Ox̂l(ޏ $2a"ɋ,MS "|$ڬ`6H:Y a\ၪ?G-yya@vWI΁uZce[2ˋYםSG8f+HJ.(̌"_)<e^ӟ ]W66'CXzf@Sv;BΣ!Y钏KC"ۃIB}rCoKKxjGY6O&S]0 ;yUc8c1:<rC^%9Pho`_?j3y(ݤ9r ́Ԏ;'߷[`Va *bIzХOCk6(\H%?v:?\ ڣ|Z$DglHӛq !μ /=p unImί=>VbHJ~OJ,AXaȒdЄDkakLy E%LB*Z2 /Nmt?{՞q\'"c,Y'sw@8723 C.i>Te /բ\1 rg[tBZ%ut] Qy>.T=F\K.9:=RpkDF Hyl1d7.To4FPNn Qvpߴa/G`9*pjb7m ƔzLB UEՖ.ehb zfQJrJABT09)^xa+L|K[&ilWhب9Uv5t#p,LG^2k?C*fDnNʯ//(_K~5 F0 *o/W"ATI1@"\~q)gT4*'h= 1ׂANʽZYmzLLݥn"PS5Қ+]M+vG̡aⰴs8%r!} ny0k%[XX>HlLcUC~@xHR qܱ+ԁQfSIAC&o[Fa/avP]l=#~ֻ)n{"mM|TRp [?k Ga&@=ѱ2!6 2k){Jxy[wυ@t(OF :7^oR㣯0ϩٯ9BGӓѭ_ux-.KpT]w>F޻3r˳_yI⻱ǃ!(N:c8TZô~ߌCwJ͏X29~-:;5)QcI%0: [Cb~RB@P0Ʃ\pSUMyX$p1i+U=aE_]"y%rKp`G#a,])]'QZTSq= 'AmaGaƴg1.sZ'Z'r͑IzU0*":K QX=&QJ"zpƜ ╳5R `ZHϪ?u䯉L+Fy_  `J|'ʟ~]Ό̩lA6)3C߫+aX~;ih@ З5ޒ՝pyW*H(:I_7e۬cAqq!"cUb\Ʊm'VOӆ u z{E`(Sqopq ܃ũRvl,7<|YLF1 gÁMͱĔ:yDnI O, ʜ0$M+&ӛC)h:%t~z6O`ޤasJP~2?_4gTɂbY/sXՍWxY;V$11wcU2Ϡy"qR֖w,h_ag3ҵ Ҩw1 Z9B*Dwb]~n##htAj Fc]9%7FYź.rLN_⹽s l̺LxsaZAg, $ɲ6^/]KB]jAD~tZkE? ]jL3+4~RO.&#l_w A[%nC]_JSq5P5HI~(aN7:b,9MjS(S~&4fsw=CUoxت+iNB,|XzԅlH9Gl3F @ DZɫ厉vi<mU8aRr r(&cEfכLWxG<|M;kWz/>u9Uvy !5~Wx4&Jg?\ZyܤMNyvs[CgC^6ikaK h}y Zژ .ܵF np[JʴJq$)Ž+"N@Rۏe/ ޖ6@سV/,xw0=T(RmV8趝@M$7)z]`W pxH Oo6˥DraZԍ .cE' 'SG'nC:AJ_sڌ fd5;Ļh L6ۆ6Gy<{j^4[׹AiZ%Q$yݬ.Vh,;(./`Ýpc&")$I 3ʛ*/40D2f]ـ{_3nq%{Q `' ::]S"u ~~ 8V nj<Bc6 *j3;h {.^aPqј*~FQV:~?O^NŘ:P̋6jbQ"IнvCx{?Ke_g֊Iig00ƗJ9@ǹ=OOET$h 9]q&Eݾ˰ZXJ1%HX]W c3aQ}-G'o20q U$fUb|(7m^ܝy7 Z! Z3p8~wo Z3`T99ǞjbY&T0gI{ ?M_ 夵KEhrniΌl+Wn XfVǒoO%NŲz^>V.1Is(yA3[رFGhS0Յr5 Q^.ڥ8Р>Ệǡ_ ǭUmvul)ĊL FG\ڸDUhn= ed`,r%k:bRE_U7v 5HLFA]tErU Ęa?"NƜ)+l{`yr׌tQds[3U~k 9A=Jmjdž!('+rC0ilĘͶ#SvAޠ὘s>g-2ř|%^aHBtg `VPr3eB8Ng*v7ۯq@-9Yiz.p2iX8>ZN-RnQA 8Vڲ`wi%XLPs8 t}ЬTeF+L*"Dŵf{TiGU]W=IF_Yc _( z 2B!A!6pA`Yt45z?W!=ĻzLJQBȔŖJ˷ZG~&UїuvZ|E:dO?"`Ѣ}up<{]P"%f?B1 @Wz4r0`p9:.&¹?ѕ;{ֿ_ =-YVUCA.yNTV+**ڬr[7"_d)jaBcFIPZA}pRE% O$ըn&XbT0ZT硒+!NY$;{[SF@`&61%h&xyۜ3t {=>/=gy)Evo3!.vovKdiieI a=y6vw:= rbKcM"p q~3 u^4 7ZSt5L7K<鼗9?Xs['qk9+vΚ?nQF"AƇ?լGu+C u/9AUd|P/\Τ jZ>#jblymq*ZO    QGR2ISL~|1ףh>-3[sig'¸  ͒f ͒f 0͒f @͒f P͒f `͒f p͒f hkdf-0.12.4/tests/data/wycheproof-sha384.blb000064400000000000000000001303701046102023000165610ustar 00000000000000 /0wA!MP+bJ[Y¾U➩ %YC[;Z [.TG% Z>a1>%P{ +Tˢ/ +2-`ὦa`[-1hTQ7wR6 #%@w;& ִ^ ۧ m&f~e&`ޙAE‡:P/q `-ŮfΤWܺ% h].0 6{cPl_nDn/@>;Ÿ bxl Y48Eu9*{D o[sQkKsR^ Ko `ESF u(?J{+j|z2N v"xҭUXTTr`yq8lusg C4LH;)`#e ܹ*vWȺ Q2C TNYۍlV`?$2Dl>~<u~HhczWY -CKL0J(t;}\~8%H`D(ea2'9Vartxº @UShk( v nu]KTPN /Tbޱ5exA;]!C( [1f֍`(hϧ{j~Hy$:{/O.zjUt~غbƨD `4PeFxd}† Ft5s~ As6XVU(U`z9 oiNK 3RBӜ` Wp%Bq:RT& wTwk2Nh%FJG"!Fޭ6 ~v1&Vb;8s LD>߮PGY޾c|F9N/y`Ijk#bFmohjAsHwO qD`_Mz(cMxKB uF6* un)Ok7ұ~?Tlѓ0: `'yC%;=1<܅Iiګẃ̹_'oF沾'(%K>ޟ%tVr8l W7WL\zvF# ]0<rKCמp[3zO3(IT%r.+~xhs^:tB nm\*,U%qR+/*Y@XX/Umbpn#;'3 {[J+hBmCwy>$nK=*Vf&+.sb=ARݿڏzen rŭӈ峼$T,v\$huS9PflRq;ҩTƻ 4XgjtD z2_6>3x Ye5]`SDNs'H)F'g*r!\E{\$e%9Hn~V~&\F?-S(II豴QW)En^ݟ:vnNc*uT=J+2ՖjI.RTzSn4b &~1]~VOWԆD+}nt~;9ar^@{lj-Zw (& $c.N5(6DVDK}5kqt0|vx> Lfq20`j-(!?Ô~>Jhe3}bhڮ3Ni&Q\`²pz/R'"(beB\^ԣ$td遆D̔K7rH/ 'ǰ=dyK7OaNėw0;On"TiWdgi( -R6ܰ[SX}6M_FSq8qkgn1ኟ)0[V Y"=`╟ɠGNwٍ\1I]cAb'> vXwC7Y -HØM_4}LAkrq-"öu K*aBOȁEQw/̾{ڒ¡ʍpRΌckX7zHo0]n%mKѝa)X.r7F:ε|VJgVխ;2QRų<·p%pʘ0qw>D c4R 8ݽ2^ f )&:@,^m]W_x0g 6dAf&+u>,|r9pЕbAQKIj%'&Ik Ɯ ]r1@-UԓK|^Д{Ӳ֮>IeH1b,. ŖI1\8YwY#bQx=R|!ˑ^6[cQgͳ6BZy9_Ƹ:mݫ~8F_, r3asF>N%{$p0EI#H8׽[f1 D||6R%(C9Fek* q*+%I+zL/9m^ܜܗ/KQ>5i,4JDE$Pkv%{CN4E{Ga-JdN![Ϸ..W|*xu8^+}q߈8؍ 8WJ0c.D T|kR#V8"&bf0wyR=CeUDZ߶'xǿw8: qzyݣS@-8l:e,Yj#z0W5H ,sDQfq%6ݢP "D< \ֹ(,#6"f%l=d]Wcw3T?tSNHt ۰>=fIt(.ԑ<]wN}g5&,Rv?h=$$4hAa\ ~_ȉ_g L?ƹ)s:Zr Z*Xx/̡.d4'¿Fq5dVTlr UʭЊe0ɍ}yYĬ1Սy;γpWH`Wc\{>g7EuMsazۛLئGZuI5@ イ3HStVZM7 DPE| WcauVCH~١K*gub[Ԡ'Z*Z[ivcDS`X }leS ϐ\22Uq܀ޚt2ޕ@Pws4z/$anX1ZsH&DؚSW&̴\69=!l̰rPOA6Ex]HT'ouE(sBM4t_";*5UE@>fC\%j4f{]X-f.A2DVv% "(¹k0W?1>:m{QۋG=Rf^jmTf@Ç-lc<ͺ&Bdeȹ}#F'ٮ^6t߼DQJ1, Hg9{U`FZtJlՓ3ӵV- 3ǸSTv9E!mEa^TL RG.89uڽ{b`v:_SE"[u%` vt^8U3;fL+)&s33} v䰓>AmlkvX9sFgLן: gKwV^}t{2*ťd(;3xAh%D^R_Y&l`I^̘鍃[n!nobt!bcicگ{~V;Sn5QZuŌo?\ߣqPn_&tr_`?>{d_A}OFE"kOi*Rzku*sC9D0  TeuaՏ7J.wa͵oAGuZAka 6w[۠hǕ@SZ'z${%Ha`ősL8n "RT\}JCg5#6 )/ܰiG|,u;v]k7h~`7j^=#C'6/A0YW=C`P/-t G~Fid "BG̀+_o8 =c 4al"Z;*1W&djݑ+Tj"2 Ysqx$0lrcg(+n,V#z8Mk (#R^Ӣ]y)4xLj$Չb7*LNo@?Lk~!--SNHc=)pV囏QfX7,rŗ2tH\9򘱐O]4,z"j>dk6)TK[NY*imVly98Xp3qt]6W>ۉz%#5Q7k|]] KNr:+Bv"&cܿ`O1x pMd'|;rZOR*~t0,9kc8Z9f>RJcظETxT+ qj, oŒ8nlc3 aQr. mpHӹ+h%CMclh 4AcQddMd>{fRdCTe:|,V^dmL8/}UP-{$-_*:DM]8:&{#;zS#^}1ANn,Cx7aaP%ۊAiS̢1qEK4w@H_DtZ`):j-6?&t3H#xS|׷ LGgC&,OÖQ^(sϟVFq\w_أsY /{n6ʓ wArf ;Z)5@gD-h*3;C$l_K2hFtBA PxRY!cÈL&fc0W78O@]ͱz==@$~zl E`F#aH&rIFCֿ%+ V1O^NDƟK+$`Ҍ"nXel8l A.|$}t[1(O.fX 9ؚ"yᄄn盞@N"!$\vI! vɨuժʥƋ2Bvh4Ƿu!ƶ%!]Vu1Dnu[]0w@y<n's?ߧ?^W-XG^Ks\沋m*uZ}覊%m-@ׯQ{V>gʚ՘`궭j ˷/B7#DMN_$(  $_)FèAc R׃d"; ,ETQ|#Зb^>OjfXу)beV MgvXl53+9M*WE`SVh4,FU a|48t1]^ih=6n0(RG d+''\A)![ ce]⚉U]B[mLܛvx eQkXM{,j)tzu9!W Ϙ67+Bӝd"d ͽ瑝q2r*ꖪ2pHm*> .Q4/o'%z)k_νdtgrVّ!^ҔZMW~F9ñƯb{4Ւ4Ӷ"ytr$'Qm"dV[ɋh>̺ʲȄToP8>B B4H0x:te8[,/a+.n:Sc}ˆ'@ { 'mh%[ξwLB`JceA&9m5szZHR):7`d(Z2|3ؒW@cv6$_ϯ:}mezkT!iTZ)2jP˭MΪ?~lUK`y+(l~PؕcN ©?p4iNk)Yʭa#q]HX7p)?t0չٷ΁-oǩJ4n). Zs+ÃYJW7)@i;+Ff =HO-6*拽2HZUĺ88M>c-Se p*l. )@wv)Z \4+^-I3Rnz׆H~5zA_̯3(7fN@ wtSt,gNE 3;elW jzf̛cvFlKl&_*\æ17JeUe_iW*2H\]SܨZ NX,[o8=K]ks$Pz/2 Qg:"*`ق%j6&%+zAF[Z A QqvRc|D몽R~PJ3yɾK>pl+>o~K06t8clc,Tms&I#SGS 0COC:JTi $udy@fZ^Nސ#ؿQL7moՒE}SGX*Qbty挞 })7aXvҼ~FKNYK $·V@T=OXDb+|G/1mܨ,t)pEZQ(4t TAf$_;ܣ؍0]["^<Stx/E#:n}˻#6@1b>sЄ̤¢f^X‘۝{Dsθ7g& 1 Qh}zh}?b=0vxL}Pś+-Ht)!5`#ύ::)*+ v? nZdeU IbcYtn;:ՑmK+15/2h|RbC'2xtȇ~Aj, Bj :gK,Cڵe< juL`fhF@կD3d}ns;UA+j`J@ǓOf3Yy B(/70|}@p1fJcXs$q@5+4!9x I EmZݤ aQa\϶ZY߆E *-Ys^gSdK*hdr7%[HReWJ_׉:kW|1ёRB݅cCk(v|8dZVd$&uHbț\pGa2s~ֻD얩a]j R[KOg5n{*ASN]_sLQ@(0H݌\|1) {ip\`_Urhi%ԕ,]j3 eH:Xm%K1]eE[ʢ_ŀ{!=8՞̠rw oMH /!vK?uxD୛I5aF欯3'5q72KCCғ3!+kjpN6-ֳ|l9N I!'m{:y8m b%ߞw?N"=l)-Ӯ;2C |rfsG|MO:--(^2M `:bҨ.pLq9[i1ZDTz><?+G] 0FI!KǵpVOL[hdDd5p&z.~nsRΤPAC c5'COuZFM}i 9}l[ok5<n̢McdGB}*< KF'C㖠r ~Uz((N+FhX8Geʨ2iWI7$,jO^] '>/v#[<ƶ=so B]XԒV,e\?x_IH}(+E[C:kd@S:mhi 4x;RY3-ò.:4L"UC%,sG1,FG~ޝRٸD~?!خ"]J 1s$Y_]FB豌#_C .N;+ZܕyD,щsLŷ Ȗ&/+2p`dľU=1)Cz_lқOVLe%2LPm^ōX-Y$^::'S-*+s£ŝ+:2 ^%+7Ea x#qx8{͈bC|]ܺИ?W8CPGEY~=U;J֊6n.ed}7ZGl]5]dO_<:m!Ko7o^MxGD1h86oEd8 E jZL^Sg Cbz>/3?L^[H{*œۋ-)VsVJ:Diǩn1GoοYͺ},=6R.Rɮt=TߒǮq{ PR3WgTx֫ :?/`?l!l1_oL 3c׉+n_oU4'!o}Ë:R ߨSJZeX&2 y^򂠈\%rF<~*sy$)bp'8x _7;b!TZL)ljLljX3)^,u*esJش㮷yPpR&G qf*u#ky%$ G-@oEamU 篱ڹ_E''0Wy`'!Z~MI𙡞xxjt^94+z r4qZ5rEBLF L5{%D A@Jɳq:AddG̈́ה_"3l|dLY"ʩO+@adklY< 30xY&™+'_@wx?`[&Rgkw%h|c6<^S,}*ƪ~ĺ @@ J`\q6H&bs+!Cu='H-)LٝMir# $J{;5u~vG ,=ŽYKn|Ao;n| C\*F NP*oeP@O=8 MBכyw,&.v\Q ݰif*740 |6c%Ne&U Bf4J3ǠאaTso$f`[aat,71g\4lb%xH SK/[mݶOI%ټ@DY v(:0S Z&*_,lSzQ@j8bz2z~C.mD9h S 悍ݖr)~plv$g-5.-`(ȹ"3ZK0_߈lEswF\f؋1g8Vkh<)q0ܰb ا9D{nHd7Cf*{'`l"KG 9\Q>c ;9۸nQJQC[8R@Ѐ HW6DhDoM)QS0yZ Tw6FӮud(PobAH^; i(P@\I@d(i fk-6) =Rf3ȕ;+)%DmghvNO:^B(5o9f>0>~§ ~` j^2D^}V& vgcOJZs!8Cj{4]z NˋA~#pQT@O!5d۵_ZP<T7`\tdJч^JʠuHWy"HMi'@ߐ<2( !M%`>=@nfʭU6  ʭU6 0ʭU6 @ʭU6 PʭU6 `ʭU6 pʭU6 (]kjPgz<) Z(P71֧(zW 8V/t ͗T@gߗY2-mf~{(B|}ZBEt8 jiK(XzkR Ar =U"Xf|vOG([ỗ3[J ( *nAgc($*!R$Ex(Xz:YzsI2 y2z&phwC(m#=&z/ZfQm(CB%IY~Oui!(fg!E g0 vY]^(E!}X(mDT"TTd##mYBu}cKac[(q\ٙ$<2p .0+MӤ("z\T5m{c!ހI(0(0c12e8 TY`-2-nUEVF-~eljT(kZy;Q3A<&=f:'^.RBy(Vf"a6OL$ ( ȋ)@uN',Go $?$uYH LD6qrJ^ (qrv3;/z*D{0ƖO:=QTo x)~ uۗD1H݁hFS/ 4SH/24lN(^C C,_ۍ_N9<P$ j~ZGDGv5O ن:"aThXERO Z\!/&#SgQSd5w଄OXe L2``p*HPi |EF06wmNvZ)<_xCF+ dh_&@}td_#Fk:oC%:7h0)- 4Ra2u3>Ӛң-{Z4a3>&C2 ]|YᮿIoVᮃKlNc[;2j=0w4qզ/O@`kah]zxEcSZgdGx;Hd$!hg%%rœr}qwH l cvl}iŠ{eU;!=ˀW3C>Jcʋ cIf oҗ:*Y%tؿ) w/#4(qbj=2R"/v >$Spʁ>fbpE;lG4^Al1${辸I)3j8՘BR܀'gQBTT9H>'EmV{mMUFDмF8mcH~怤ɼC t@WX6t=STw ȯXߥ*z<#7,K.ӞK#a`?׍%1!-$EC{Me9x'{a7}j)XD翺ݨE&ʶMƦڙ>#T\;ٶ(đ&XzflI։5?SܭYGTzshaTqXҶHzV#⯼!Æ$vqZto`]#Pg5v26TfaXfg> R)m[q.ݸ-Qfmǐ[X*EA/GI{h^e6 үv{Bx/z]zE1mtU$ݹF .9SHM^Si&L ;ʱ^LIBz>dY3C+'Qh-^cw֏&7%6ko! x4._[.̅H^"4ZK4ԂwyD!l8<0g^}M?,Aq%f7ç Lü_vgp#WV1C#a!zSˠŨy=A=yýC[qpe U'M: Nb'4-!U[c/24 OݎꔈmY%so C՜wRj~SQ,,5Y);dҚJ,iDOt(U-8e=p=YVMU-m82~MgԥXK/nÚQ!{Fk27zpk IRx҇ɻqt#f4bRuEuW]*a6iiev]e ?]}nv3J*ſfRR;C~sv@myzPͶn"-sKˠeKxE CQu "k_ɡ%޽xf) ՄqX0iF`ب8fxGk#'MĖ-Qe.*gn(|ٔ4bZ+,dT[F͹9JIWG^pWi+6ZD>kDŽ3_Tp+FOG/D369YFfrzYcYg8i=eJe릱]"I@$AZ OI/j֚#[|$VϠ(YyW76..Ϳ`*v@soSu|bqڝ EhJ@g5>ɅR6)P)"ISɓ~}-дۀ@0ҭ6Um| BG?(!@ «@)׿XK0 :BX@*DE&9T'EP$=?zB Ax#⻫ugW,`4aC67bt~0W86ٯU훂sceAȞޭX z泒B!Es?L$G#M8+qM 4"eiA.T6<%Miʭ/DŸJ.:> B$)FM2ZPr'l)2]&tN}vi3:n'V1 F"wgA9^B׺՛8j- c|ueeM _R aP>D(̏.ʁ PS`k]l}Ik.@ee_BjC mivn9 {FVuRą@A m7TÔoy{i7HY"L3 Ssߘ*bXfKygC3>LfU٣$:m˿f_堐KxoVD4Dh~wd |ŭG>Q={v bmk b`xY[@LBeYBn11>-1ȈJ?36"}wdUX ,Cz4pQOcMp5luQdnlZxTT~7B:k\s7#l A0-l&,ZX3 ofd5/1ubJe%F]]Ms }k7xk^,>u]un/fHQ+Ԍg0{ߛ% LHSe3L %l_9OѩFF497Jwm{ay'е*ߔK1jJ/Ɂ3?(uD{/` u0^(&ƫdI[sWs#" RIJ6@cpHX3M!ʭݬ"6&sJ U<, H2ԀˊQgoՃ;'aÊtFs/ ć-~e*M|QPh/65N LIxBmM~bR;7# b:QuY.*"<cfT8`rՇR?ulD2Kr1_4(R'8U^"0ʲuO<{a'؟k.Q-z2]D:s~<ܮ!b?lR`Thm}/iWa9zÂ01҅;b5̑CBpZyO*5!_(VքSR6 XUUI)}×+ f$"Cx0$Ɇ-8Ď> _5^ ~-+}w1 ;L5…II6a(B[ŊS{=@&M6؀w XFLFt&̬>^lo'dnb:7nMoSvz.vu]颻,-s'"!;;7ȫ޷+Dh1KuFFys~2[XllºD!Wȗh00m6 tdAS\8+M ,৩ uLfCTYSal腏4hmG6T IOEY)£<یcgr˷u!@A5VM( jZB [1=7KI>!4kψNKYBMg!7z!/5\ܳ7)4&ΐAVdoOɻP|ba K=W.v2. ]͒vu ӻ2upSA4%?΁rsZ螋q⪩2VS ɪz ._Ğe>gT:=:]9 '|s4k3אmSc[p )L/wJ//g@,? ymLSG?FG[~f9Pfu "S)&s x3#"/Yei:)$CvfZ jZڋ>8Becj=SZ*P(|<X!?n9)SxPV+_5^O~mE,|k/,ӍPX6F0ԏ-#}=gU_J |b_-W`=j`u釜w5U𻽊9F pƣbԸGkC;sѨ9R6ٻOoJl{:@'J*(mmҐE~DaT;f~4)AG K-ƠլLAK*LQbJɻ½e.Ϟ/v΋ެ="ʌ#OH T]P189 $8trTlоe>d"]]u|j8~wRc! ֘%Ա̱oY.%~WͩhrH+@"D5 tŦ=\?ɘbNwGUGvS*t 61yI#!WHcJ}/+M4[2l푴nP0ͺYqӕ8lNu $ʔF8sZJ| X*O_R9_fɏP fͥt$lO>VE}ph)-6ؠZ!64qCL[ j( e`_uxćޖ=7qDaA+0l"mj<)n4HrXoݻTȱ@xS=*ݲ>ZYN@tm/^dd?'˱p+:X1aO\g̝E3_,A{,6(T}`D\C vHGm?Ƚ:Pn!gs,{A=2=_ Cڵ19 uG /Jm+\tlGi|K,4F!ҁCoh' hT|1h/wbx5ܗaIdK ڙnz j;a쬠ЙH hlS["y#R!,ZEergE|"&YEy.&!hϦq̰雄ӥ­w@glC@s@$dᦥu~M!7J.бǝH)y݇6m`Ql X=h,3ţ<5-Ki y^ #[{IK_3oqۛ#htr;x6K}mzK^b:!ƴ&7o&$pjd"RI W-HLRpKKpJzYHk?- D5|pgv7kĩRƞUQ}깥ͅQSc"0Y_yW:WHحGO^Me˥/PE$v<Н hhRfI^d3CXI"U!ʂ"̤SԐ%Du3/IA8A4xį`C7B }ĩIt_n~.dgR3kx=:CAF(u tu4InYvu㍳+1ᬞ:E=)xBdJQpƗz'~6*" B3CTn< ʒ@%u 篾ǤKH.BPY /z=&<<5e[%d#\6D~OK)2nh0ccq%!W19gW-q%Ψ->J6oUVkIeWOYkd1/MaԹ;ă`P'#WJBq8ŞؼS-AEBJb?3YȋgSQTԭuS My Gԉ5-!=ۭLъ]KhI$}2ͣ9{@әkt Zw6&. H@*z wPäu)qˎ;Cx1|/ {yB-"Z+ٓ#dvcs?5o7\&vʠ*Pm)u"_mvsJ.Bp E`vAWfRH=9wly E{O?b߸NسeUHR :thÛyRq$$#SǠٱIk)lېrO;4cbG=>zmTziCe5S)Ӓ=Eʮ۬d] boBOx~i1ծ3Wx:bCm5:AEv='bN!g{Ѓ[MsM*['q#m c )oPkDQZN\#$)Ԉ.d~f#©uUkIf!-J@@3Wض{WD}扖y4Jl#@h60}֯b:҇NuMb_RkSشBAm!훻p]ӝn.de;euZa}qI|w=gӓwS6kH ;#9WT)Yڣ9~2x\oB*zs lm/$SJUƀ p9Mu!LӄQgQx]s(RZO1 fl%ͤ4aXΨ1s7!Jz/ɕTJwr{9D鈲pDV@}ni^77gNkYZ {6|aH LTԾhȺaN +B/;@AJPe)٤= _vOFB+)(1a)%1^K*wкI?ZȈmY8 |uL]` R{u, [I2'_hw_\q7JMu\=s<.zBvA<^훝voAe)'EvkdӭV,5Q?jmbsjB&{[n +Õ!<@=b@qC o6daw]z"бܧÃ刮{6;JsrRs6/BZB(OLGRM+9FduUzo?&Ko^ggF;r{TtǤt@+n9 VrpVF`6B ׏D"?o|&/`1`| 3* RU pGi4aG:"-^(Q%m6C@8,d[ТIm{ӺyIF7ka}\.$e\jRin]EX^(kxㆳʳd iq p :J9kH,ӭm{eYjN¬m5DLz#Lnۼ)J1^e !(:=RLLE}w^912wB(%:RZy c1bEޡ8a8d Tي03 plE 3{AL6V6epE-K@ˤ-*]-VZ7iHS!9U|c"Ze'ϴ:VW#ge_,qr%'u̱ap0HazՆ@s/Dz[s zDaCk(t0Z$A7,{؀m>!(S\kwΏR@iHR4p~>D WYHg>rr8m(~ko~ g:PTmUuKlU@,:hUz*M<ќEU]B@+8T;HIzbU,k,rj Ʃ(;pu";6)i;t>.-LkU'G/E(AwH_;nһLZg_= ,V@Dt馅PCL}Wo/w!# W };Gȭ'XO(|Tۗi,~Zrwč@ ͆5DՉ!r1;NWb R2a3nZQT]h%зB liJm).`@zӖREJWÚkE T@ ؄{ -E)(le#2;,DH˾ޤ-C/ZkA$q֤tA z]'Sw-@ "h[f3dIh 'B +n3SG1-9@F(>O=`|.<0|`H(&PyئƐ'yH@ kOA'ֽpzas4þ ڧ½ GDG!yTJ*(Bf5As'Taim%B0:h;ˁ[EӇCrX|c]an@'`z܏/( Ӣh]eϯs p9)7jn b%1(65l(|˹oP*FoL3f; r0Nں/"\G%DG7xEwUO8ZnS@UD!-vӒ~ǗC'BE)y?<]@ 1X.+v ;"lb(-EdQ2&)8 @Q!,I@? Zk=1;Yú,:xزO0O,<<lOhKiU+ >S˺beT^Ye%UTu|,<͏N} L*VNĹw@ӖƟMȭEVw_J"~)<i3u4$m'I8~},b㽣0% ;Oz\KHMa:] &9[e!6lPYrf_ĭ dWok"@= .N*D;3Fyn<- <ˌɉ JËp!v?.([@^w(15X էڻ-@dzK|}L6lZ(U^ .?XAfg(6woUr/q7hs `MT7DXad2RԚ{G"W~먜_y|aE~k@1@c%JC,ٓ2(76+ oU16HzɎȴZ^wFF&QAfmK^CKm~c0_iTe lѝ:0Q~MGf)LQc#5@[ME 9/l.e,#l6ݠ +&ي"%^M#Cʿ7b3g$L;XX9(a>51b/GTOTϗ6:vfS3RCX}Mr"@dw͒.*@kgV%%U1fj4)'-u`S:ڀ4Ió;Hw/(bD>Ƴ51 8<*~+ƪlj (leqmSX<#r !L,=T$葮!R@_[>l =b;fif =O~!>f_㼈U. ~ jrK#B ߾6sU %{RS8x_{ޡL$~;M46 W*0"xGWe}:&^_&$ËxěhfT;h/X/*Cz(cbFs4=6 J.ev<&ĐȢ!9Ihcfm"ҡ C&hHX;cOC{pSyoѰjC(a+^ t°тS5};FT@s8Hc@z77ryB-;|~|P{K| ,p*=a vBgEWACd->]?Nm[uN!"ֲha58;H31gQ*2ao1t@)k{EH/m?@?l(cfYJNtLu35yGpBϝxHQ>̉`zY'[+ ˊ̗Dsa]ˈa3s-W(6f/MɎ?P ~2ΓvNKk` e*P9_) K/% Y p[:mC3/7 8 C+\G^}=(&u6];-*oYڂoX,RB 9KAֺ /EcE̫:GT<y=%u~-^L_jvk_~Rٲ, i4TQh ҿB!;ge43Ǯ՛^۪ḳH%uۇCUY%®2 -7Ht45^q[|@׫c]o:mb5_wP79bS srcmҲT|ѫ^޻hfbsS0(` ~Z3j$ (F'27zPui~q^+P$`檟k[v Nk.ь;$/Y 0CWl]i׍9DŽ#e{O#FʧcFaxvj"<>ϕ e># >@I9+4c3.TP%u=y ;j~|0Aُ of 8n!m}B{`]$GcjEu81wOMwx{sG|Pb_dO1DTрE RsG@'ې#X1\YK6s΂cғUPHZΞVi9uݕ-%F"Y |\S=yLoJԮFijbU R, J&N3sCYDuS&ې3} q{CR$ 'S6D%kߜП"VKI | e [E2v$!ˀIN@/-)+_U1i}G,}7<~;-tcP l7yDAơ 8U8dX/<%eQ #ݒ}\']WvsYsAmrf`<5C!v"wN2?wMxǧng?Җ (Y$=L )8Vʁϊ {Ay0*rp_Sz׋9R[bn"F^)3y.6\MFf+q)2zMHspiyrAQʹSJЀoҁ.wqǟN T'yaR`ǎ`56WTŐR)JgBڕg=WUgB3bh.7T5D1QG~MbJcxRTs K'UacPf ?(ˀB~M%KS10ok2 DT"bW/ܗ0?!U:PdsP;[l+,['Bu*T獪'4,'O֪A`5Ows t+FO-yEoGr>$p;A3zr ؖ +Kwͷ-)4n{ <=L$ Xv߆R7F]T]1ˑo^-U ݐ3D/*A֛( Т]?8&Ax16%l HB"^oc?ܫ ndW7_(朻ŹAiar2B.|(w^D᥾O:A ϼYNz|J2%K~#=b|p~N t'=&v& _ܱVGq;Bn&|b!\FAzOY_n͌yyiEqDi+θ KÜ:|vn6/7J:_:o|t^/3;L:F]VX;уژ+܂ ~>}׷?"u-qnv{ZTkn-}=P/ڄKe]r$Ymܶ孟H cG9 @-|G>a̱ m*,V &wqEnᚩ }\ίI; GM-[ &W5RZ&a@ MB-&~~RG%J㐋Jr7Ud1~1b/FozNjtprD<]А 1r3Mn=fŞEXŗ !}.T[%jMfjˡ0mxsՖ4qֽsHm>BsaUDRGN_٠MnY|Z'4u" hDR1l!xq*. 0*@WrH ,MdO칱U  r ![Ww7w ʄHmb< y}E s<4$VS%A/ͭl7Z+:$sy){?nsNEna)"|*6q LپO$:CsH/xv]pRn|Rr&/ZmՕՎGoOpwȼ?)KEH 1.ua4.aɓ#&xWEC]h FhgEz"8lykflNo!phup;֏D}Ӹ@_W(S#~lw? zG9uNZFӠ)lD/a4<$drGs*Exҭ*v3.DE02zH+nmVx)aH VG.O& F`9sew0*:^V/cX*7'PϿ Xr)P 4+q6 ngoVOUV1pgĤ8G P-ùVZ_|v"*lԟ#A HKxr*vdn|ؖFKH,-12d~Z.*?ED5>ijA)6C&;eCy Hp+$ X)zY*&CU*D'mH7:E;S*;<<}2=Q5+"\po$:o؃ΗȪ_})=ts*~n@ܐI`8jXpzX Lj ahEJL?zes\ %SPץ]>cZ3?֋9 uқb+lx9oRomUB< DǏ٠l3n,"M%h%x)%XvüxF3No\Z!K\l2Hv| a:h'д+1(~3rHPXKoPf2()k]8p*ЕUP5|Jʩҧar#&VȏO%Z:fEZ% Fh2%6\o]e%&.TMY:!Yza_/{ͫh3%dQ/Wue^Y[$];C*cUX[ےHy GzcCD3,JsEi|_ќ^ ԇD`t+kfA[]{BՑkEd|S&QyIEh(KfOtn5|nTs$O5?귰߾juc.gA@#3GZה\(Y g-`\.!5@aŒ㎕\φ)A]stw ٪hx~}-ͭH[kWS'0#stkQ+5~otL/+֠kb5:0^, }3tqD:򊳙{iʰ=(\`O(2dR:=э̒\S/HL:X-8%}-\[7;g UC ׎HkhApɔ "Z<Op#ٻHtإ Vh>P[U(t;IP/y'BTh|EA2 6в@0[HD'N8=\`DsWIO5Jz,Mx^,p9~MS+IX@NqSq;}ɦ!8gy T M­!Jf/+ 2j gA$9ko c3c6.R 3^ECGȰxfmhjmBARlds/5yҔΪT7&-P#99aGV &0me$\*,^B&¤qɢJucSw8"ijwvy)пI.4iJ2Jh)'8SR18y,︳6d퉡od>K6/*h)v03"Vlm3PX[&U3oz켽ne9Kz/ZЧƯ*>r`mhoZNKX{͇Cpl(15$oO2:PtNliN6yw n#~M0U\l!ہ@X^Z~|D=E62^wng'D63N _RtlW!u t(\_9E7џkESؾ}X)m|VW:>ЗCr^V&)u<h@?#lD<1ǪЦiTw\f]x-D"uF9*hR#>,O!U&⮉TRIȰU_BGt/ejb UAޏ6@ %zOBߋs tp;"Ý;Pi/4h); Cy2V=Ä+YJ''a PQkUV:' Cu"Uuޣ1&ĪD~( nm5u..NTd Wԗk|,]52g]T5gDk^!_ItZA LA%2i|K*wD'Gn8-1 Uicb* 1M\RʣWFzL#䫰vǿ]ƚuFnZ]=?Ϋy+Q_-ANYm&3(~t a$a̜Hb4oZH4bmJ"OӶ53Iq59V Ae !ī1ȡf'*ƒӫyl3ʒDŽ6qo<@1w|Ld–~3[Ӆ{qSY3$ /W.@'EZx_=ͫ;*T9,c'}3Yϡ&xWG<_2W,G-{nU̾[,xgBhpZK؃t2y uTX/ ]:*~@,nfY,h%,.'dX{%,h>8,+\%s+D: 7TˉOeaXmI*hfl Vv( nX(٩+̈́w.-&9J赽$H4F@(6ȓ{$)8sN\7z"1+2eۀP<;qN+~o({uLL,wR7!=v/H 리hrl4avL|\lXL$ש~dj(j]GM8 0 " %&?LŪzd;2 lS .$3yjoҒii%HqGQWwXJDLI:!bܾhߝ~crTZ&ؕɀb]|B5'ѬZST8H`%rW(f\xS➙YUAe5HJ)ڷ6F: %xijsoMx (_"8o:E䱲3%E;1Qç-o6}W%d9w=OpkIJ׆f-YUzVY W8Z)e$XF7XA5}e E>i۷לjXV+\7m4w)­ݭiBdysV 3r$;^584o5trZ^̓n+z4?*9c#"$薽pZ`w#KEW`; )5ea:Bc,^g#o@Z+3fr`#CW Tm_^m<4+1a>0f<((Kvu=kn41t: wy6}]QԳ%qLא v/=oF~~"wkLg쒷cg?TkHKR@c=.:ut <(bb1(41˗WI&TBR JI)PQo xj<բi76 k5;̡DZw$PQ]xUwTN#DOejg=|ywIuy# 4*]V&= /&&׀mA:;}iN/Jui&U>9A͑;v@8.(ѻ%</ 7AWRI[@KGLspOaPKV9]Bu8N +Sߖ КȰ|?7npdڶM9W)<>$yxOSY5wǐJ՛iB; _~UZZwjNq|s%BZh`v=0޺y "cW=kOq #2k]ҦI1!?b{Ri5E^DVoxfu&[r_QZ7<wQf P#;˚ь1ie #>JLNg'S+Rhɦh1ˁB,gjlMk;^c`߳ d_Xeq:3E<Ϋra\J_7zlu9@YȷMXY/-:H跇5Xt;2Hs#Ef57[teu6aV xa+{02{m_uѿJD !VN.@F\%X(rTԼjY ZH.3T`V~ -1^(8{7 $ΫZCta._gr `RoJ U*XJl^݃#S} ݋UX4?nqa.ӶEۋ9 I+="< /տ7Z`9D1S> :Pf߭"q#$Uc{ ˜@h QF1OA T(~e}@s=,FG#SvY=qmrv-IRbL0bxӖF(Aj6+g__p|p{NI/co. ƺ6ƌ%xs%t~($9iuXR"{nZv+`k%~ }K-ZX3gi orʀ͙\|^ $B'w@5D=Cix-;a)aa!nW~6S J/ەG[~Nz%JQY0ہ:WavZNrM C^#yZB)^mMzCt0aXE !p rJq9=S ٖI r7/9g&lnhgśt l~JlNU1E#|K;AyT" -]Z7Q K\HB4N`ɥRBǶYA/k :y*P$ߞf?{W-`H+I^*ٜۉaq`>!TŝOņ8ĶeOзQ Cf~!>W0AapO;"6W` jh^)?_" Yl :ZZa\8zNL3KO^-yS :c M<+$~ 28k~RU) ]}G "Rd@7Z>t` FҐ! t@jB1Kپgy X[=f PX:YUi8,BSsRc9߻i9ZBdc=J1>S^0ӏrl+ @ة;rGa0~I`L~y @߳Y8>[Q/BG VLPfӎRԟ*¢k剿R'#k .hf)RO-щe·캰 KhZ&Ԃ@m8D̋_4%&Lm2hCbFe$5 S ~Hs=|HK"96|%RI;Sy%/3 8 *J!BeOR_>eHk!k%y.Mz+lTD$I~svֻX!Nk’Ҿo(.W֞sP-KaۼLΡ3hVJYo)|nW;Yp$s*s_r݈cJ?fU'F #xDC``r ԕ7BPA/V.ARb$ixDXb5 u[wB1lm.r6?_h _w,"94x~v>Yҙ2 V.>#IlUJyESߦS &u?v 5(0DCY-1u2;-3[qi0&jiK*#׊Rf|ρ<,`G<9¼^NSk?W.Pbx !΄TֱſD 9|Z`ہ"P^X*׶GXv;c} SJ]u`,ENax0+a=a05+۲__6{71XvQ]kӰH 2}ֹ'{ݒŴ /;PzOZj][֙URXc`zxQO;vąu23k<@ iҙVy4}"^]?l]G~p9?G L Ƌ#H71 hkdf-0.12.4/tests/data/wycheproof-sha512.blb000064400000000000000000001610251046102023000165530ustar 00000000000000 /0wA!MP+bJ[Y¾U➩ %YC[;Z [.TG% Z>a1>%P{ +Tˢ/ +2-`ὦa`[-1hTQ7wR6 P<$t*Ax5hm̌]V yphʶo:~|ߞ&\ Q&" {fjی@z -CKL0J(t;}\~8%H`D( 0|ҐO#K @UShk( v nu]KTTaT86<{='K#oNK4 [1f֍`(hϧ{j~Hy$: LG|mOg^JT.y&h{]1| (Gpjw ^FQc-NΩR+s Ft5s~ As6XVU(DhqCzܑL?& 3RBӜ` Wp%Bq:RT߮PG  `WD9)`_0^MteS LVQQ=eaƘF]<( \A֝$$ AN6bs +t3~C'(? ScirI(vr'DS460% ]=8 bW o;8ԯ(+2is[FT<-^qp uq_ZI wy#>HwO qD`_Mz(cMxKB 7RPad &{_Oq;M@hw.Nuڀ֋v `'yC%;=1<܅Iiګẃ̹_'oF沾'(`sYMcV̄ۖ6CR W7WL\zvF# ]0<rKCמp[3zO3(IT%r.+~xhs^7mOɳDuJ q~(QY;"g[niU~+uRM<}3d%)h {[J+hBmCwy>$nK=*Vf&+.sb=ARݿڏzen rŭӈ峼$T1ݪ[mcع50{i>դM"n5N~Pi 4XgjtD z2_6>3x YevbuĿV? kp_Ҍxfmd g{ь~L!_Fxg/|3FueK&ĞD8Gh/HVEtSTTM&8frwc7a1֚P(d2/d)b`B;b|n  ;`#<{w"t8VDfI" Z[iИ(-\`vipDb{C>v/|?_N1{^S|ѹi` fLfdAj9 isMסKmˡRkL옌#읤Zs1?-~PYVe3b=pģH뺶H1+@#?P "_,ԔJmN!f iˋ,_dhh~%t}F ؾ~OxOaE31L;}N*̫0^G1E$O4Q>ޫDjmV߭'|5a54G4_栬@iD`Cj>+jwVLDvK4k,WguGe WYQYAݬup)6>@?cQJkCu@s9:$@kQJ 9eCkzjg'! [^0aE)gsJ4uTb#pFԄӂ}!7oTJ;29;.9TN[Z@س$㐂.0<7A&gԵWFH]9HxBbb6WQy,5%9\cEQ^OTqF(9Ah˥ASk O[PVȇ@/gJ!oܣC^yGɥr vn$rC]iW3a1]s;$Z2ľ^s}!2`s,sTGASlr= E4CٖKa6OGL]M$ZJt!U4a93 [ȖΣy_X4J߻jB3%y9Q5ZU_>jhpN1㾁!C \cfϿ;}ox.ք0)éz!̔L8(g8o1řg)͕3G\Az̾[fdl)xM Elw;Uav7"!\ Kd k|CqoKKD'IGe"!V4 XS%̧hp:I#3g:loA~ HFYfoZ9w}GQ.)=Ie~ 5R*/ H4ЙE On?g$xa6mcbV=?<9<+q_6N֤^J>OɌl.òE_k.kxEֲ))GA lD{}~4E3ʵ\O{FGV_>jܭgR-#`mE1r ^ǃ>Xde؝{E$mҡyҶɹ*Шj|d[J Orr*?q`oj5M}o;_jC6.fL#ɘiB3C~g}@,H@# yվUELM3?KyoB"SlP{CN0 ?R~lwKK̛@_Kޱ(?i ]M=3sb\%}jGv(!.܇$ʔO+(`5` 73 Zkwc4 u{t<_Y,|REYp5%ٰ1h$;`cDӋ˃@%r0͊ ZG.wvVB G438n)9OVS<,0%7= Z=oPiJEmУ}Hdf}^$nX9`km>#MzF1s*pax.<5hز4jd).n6~B")Ѣmȵ; _MӭaoŎ9@:KZ4f.`Xiut.|OPrS0 %g4C%+1*ePk׹ўHRj+;H!dc5ՉۼcH-yRxh_pijLZL>#}(}(`™b`M|OO'S!1j(%}<41 ^ RZIjgC‰ o\w3>ݸ:is^3r~WNrw)ZlS=31;TmP*56$M.IJ5o1;U3B@1dN< dmanHI⢑:yB:;s'%,ODHf|%|G .Z'I(K-LyZ@!,1bjNEŞ*k;hz=r'f,JAz15=Ow dɾwlOqs"`w| ,nc%ѺDHjU礚ۤ-}M?$a_n/?d5mGYX*Iā٤d^s',5wbPz^_ÿ/X˜c=dP͓4p<B>2gE0Xu(/@wY}4+i%w:{C Px󉅉onmS]+ /%UnR2 bf%<emFZ{ 1od[0 y8"F2XhN3ہǪN-d@F9HPXkЖ͡@f;xM7/vl'HQu'ܯV!%XlUKǍ6}pA>* k+z&jMP]9Ҕ!pEq*`:]TMqԡK&%K Wڳjn(gt g#U;6uߖL,Hs }{d`g]6Tvg3*!F^!k̿|rR @+۸,UZRz?Ǘ%a mp";fљ]{;񶝊]L7tҙ;™ ȝhgcCGj&` <8XYiSN`=w%1?N)uw(;ֈj0C O[/S%kS o?kfÂ8!*{yK2 sbTQy0@<}R}YSTjh~~% eҾdGSwꮵ=DTL^8a&,SQ@0@\_K3s`AjlT)&.LVfYUXE*`fug84:db=޾]q!,L.熇exr?2l~Vz\Krm 7.2P\sÄnu_p-9:.gLMCئnCY>1j6@hH7Qk)z!kg B$a"x{5N4OLQ|*I*?sePkO*8r"  f+n4!q @v!~B"AYf 96 =>^+I{#Ë> *]Ěj(QmsFHC}IVG/&*8NBD<5v~"6d %N&)"L՚KEXh"r>=*W?R~.+kf\ 1ChAtS1ҢyFNckE*o+hH]8#+xF0KσQ1H}=*yr0Z1Y݂Îc%@]y\pن>Gž{ւ7 "2յ їI*Qv⒮/iXնnI(Txˇ}SSぺ[~Q z5d 4$^3M"ĬBD />t@}ʣ'd|oPgR$Ql=Ʃ셇i7V3x~N'UbPUhQly-8MIֹ?LNJc8'v_O';"!0%,0 wz]bȕzo[ *J!Zrz^ cƾJ/F/g&ӗfݾW&_d)OQ/; X8d = `l׀RF'Lݲ03QH920lQX˼6N'K5~ǐ?kncwY772 X>exćA&^lM!{}1STH̜-ò9ô%HLÿ.\Kߟ*O;V[He8we QCz *$"A3V] \W6L4(e4L|\\ìwӒo0_ahh7c9x<DIᦝoJne#p"4drd&nn]X!G  (ٔ=!``q | {l#d4Xi|[ua< G)qBB^Pj r;7VC %J~]q'LZ/, ]HP.p+Pk:H/VvֹL$׌o\>ŚKܒdm2;GRN. s]ߘv6YgacYRS[Q;5>MR08laEHC)U\ZOtk7-A,?(ZQ a./վ a*:Z'>KrqBPo <ɖ1%' -JWj& v׫,n=ZxC#^ ] 7n?(x,Y ^@Nu8^qYHq)y-GRk8G4طks~ |% pr>iVH.`LnΥg:?yk7-ca=عd? -SH'P<+>nuF(@n&f서2i5kg]9 maVrxb!vvлVu VF[q 8*ÕkIU8{^i'iؠZdNv'cm5"DpxhJP&K&ksabPRKT7WcM($j*/ꑿF#L`lagrEBqZry bmm*x2ڶc^Fr}'l9Oc)KTԷ=p&k>K->2RpK!3;\_~;v=w (d졿3; s[{ӹ=@JJ5^ "Tr6;Hg+l.kPK5__akuD3sGgXLw l,? p8蓉ՈYeS*倚}WOIUK `` T w'Y+g '^-Np*,ei ~Ԝ T8u٣vYˉ)2?-(ZČwW7_ 4vs p~ws+f7`vwNplqEk1ʐ{CxHé>%%a~MInI$Jͭb.GFпmG%7bT$ dA} 00PGīR7@x3g."ښAӞbOsgaarqA-$Pg+Z0M+9Dؒf$”K\JOE}X~ JWmdӭ_wrSV K$o98=6;QK^>ʪt>9<]_qoe:LU٨ *yWL{ #ψKj"Mn7 :Lw%wRT_s\- Xy$c/_g^rQgX&FqgyuO⭌9}kX6Pߺc:͠=BϏDsBm7~#EN(#W8"m|)Q $%| %ddI959I^++`j%k#^()oK󾀧АbiVxùi&']^rIKKkov*@)ϊ4|Pj ?QI-On9YЩ{ǂe(`17,'Nwݢ<]idܞV [gL-b3abٻ g2K9ZOtIg6%Ue>-ξ>弱.}cY@N&$9˫H'2q.tlq6]R~eM(;V}Mi ƅ0#FU\v,R-%-Lk@mwنPON޿% eALI:_L* r3lx{w$ fj$h>0pف"u9"}T@BQ|/Ia-c/1ƩUZr/,vd >qt\8bxg 9Cżyavg> |T=(1_h'NhP5z4{6\%9rUgaՍA]ë4!h8Q 6p{Gx$FԃFj^^j\)Gvg}9) afS|."bjbaBW&-,IՓ I&_Rbb ϓ "}l ײw$sUm0|hl@rZm?*tqqttta.ix8O alb}͖9H3n9k(C!z3jtݷߔC"@QU30mgysf Zm_ڪi9Z%C\~o@o mO23'6dՔgG co8zU(}O+lV V%3>~)R6r{CKگ<S 0ONY<9xĚ1t^2!SO =4&,_{rsB"7pMb5\;샷|)7e$`?6(10F/{;ª4h(RԵ F2~9+m1Xi,G` 4X)#nDy2ҫ`O=?ل=}9A`6.4@z=w0]eE%s8}u-`VwA/0 \ܻ'TRV^^oPuT , _K&0T[?FኁK_^<:XqF|}^A2m鋷! ނO:gHƓXXü ) fy]s#G랿fAhKy^94Ld ³3w#R{ i;/B)Z[s o$B8q3Ox['<͹5񗕠'vC^ 541V::Ur<4DfKH LY]6edgB-ͺiN^_R|R,˦+oR]wpO f)-z g3pb"잿†X"ի3ޓhzױ1gbsKJS1kȟ@!Q5};z; 9e^ %p#sc8eϯ5("6Jїx0Y~)M;FWe*XK&CCfAVVjY{{+\$*k&q(E:ö3^{:-ѝL,t,[xQjrmdi0lO7'ҝCR& ɔP/VG&NzJ<GEUTyW2E% TZO%Bc!f{g1 ˣEQ{,eџduvXf=5d:p[ތXDvq! =GX9SBf+-\~]˚&}5({h"1Ĕ= u(!;T%>/+ .]{k9w7&tL$ טZ-ML Ez 4i5 }1H_Cv? 'S6bZy 7?CbxCڑP̙$K陃|n۲{EvulDoV'8T-"z%X"hN-TMC j^IsԺZ[T*-2XR^+kT?)scSSHzg#HbBD[$&&l=grʌ}od`JCF67YXr]J,G1&YOT@|/0ׯ`nCo%x$Wtw-ĮLTj Gi>w5{Vi\ҴgƉƒ]IaRM=LDuzjorU[ WT<`꧊_H7GȐ u 4z͞Ie}R;H_><.Ǝ8 Dr0Wfp+r@{_ #.~;'֯;=ݒb` ֦tb-7hc8N0gV {mA jOq;S.E Z1㭳9ah1XL eP!dVWfgOoSb,G+UTvj,&ɏ#R #^V[H%E Ȟ(Hs@=8F)CXBw>*-^-E'ł=yN<]|ZˈVX܂DIQFwuO43&d%) ѡSn{l)=ީ=s  j'V'^16 ffKnZ}q@txV|sG_[.Ϝi),**R4>qBnM:B1L"AR }Z8NGTҼj!d+l.33 nhx$/Id:tI:ƥ^kC%Xbsᰓ9j-'w, jFCXZݖҙ۳D{KH )<hU%D oQ.V)5zt$j{khsXR nY\mח= ibWxӇ7pS>I ̮M87?p96ÞX\g|h_r3 r8j> !?[ZP{t[z9 G|04Y S(@Bx w኱3sV)ެ|Lgx[~ 2҄H_*alN&od᜛PQwz#pI@u|Qcҳ'9^zv1~+srͥwG &EL 'Z s.7rYK,óWLXL(8&LUY S"9џ,%ͻeV?QYaPMo-~xC)P=>|b"&{7r| Xß+^3w/A2O:v;ۺ -,H)*Euj\+`Ɓs{Z5]vs9JY60p)‘}u=̍1"wXw(2ȱ4>lII#ɔ{Gdt7 m=PގY<2(5`ܱjLf#5ٿO9M-xSYm4}@{E &b;T(lEڼ.LU`Dk3p^qqdM2E*i[EHa3kV툼agQ U9ь(y5;^M`p:.M xd/gW+Nb {$Vԥ'=r^k=M!`? svY<+#AV,!Xwʬb0e)ވd*JU)>l6塙߷lXyF^U/cG!%}@DȔbʎ(A%|C"~ PY!ɋ)~}:!S#xuo]/ilN5GX4gMȻY PvK|;tpٸ߰&:XuET BTA"Q#0.dwBk>mTyRڌxE^X/mw>커*(Ö|f?d>)_ IRMK("tаO69/Tgr(=IZ<-dSPЧހFM]zLb[] ֤'JS մ=@Qf!\q, 4~7Œ3'k!9Z Oc2mqkaSa_)_n!/Ыu(zW 8V/t ͗T$xqѪR@ߊ?R _(B|}ZBEt`U.@4byou`=;(LUm!*m\TΣQ(M6Q| d([ỗ3[J ( *nAgc(E?CYRE|aSV(Xz:YzDe˿1UqknM@8ཱིD J-6( ·>sI2 y2z&phwC(m#=&z/ZfQm(ѦDi!E_\(fg!E g0 vY]^(E!}X(mDTqeJWVΡs)f%>sZ31s(q\ٙ$<2p .0+MӤ("z\T5m{c!ހv+;\Eb.; x:mj R)8x|vrSuSj\rOl臊n7^2At+M( ȋ)@uN',Go{0ƖO::u&6eb5JjB !렻 mKĄuI|2%(^C C,_ۍ_N9<P$X@놷} 7ӄ6\cG& e)N/9ÜJ%LV`ÏZޱHv8Hfgp B-EWALe;xr9Jq􊿼hZ2@)_x|$9ul%ٍ=mKB{qHg X6eY87 vybou k6OHn$5|k^%;vpDGlFk[}.1O䃊YTXճ L++a0ԗmPBsr@T}"5hp'QN@cEL,iV0<@88 閩%2e9]4ec|fHDoZLP<޿2-B~1u$e y9bZ6H%fB$Fgk_|P.vT0 d@x ޥ!%&3a|#7|DOK{iNos C;?0ۙJ_ah|dH! $kwf*Qst嬈^e=FBqVk V鷃BoU4'r|_?iB*1lE"7-~kXE ^)bV'Za~kB_p|u; Ϳ$=Gfmumt+@~MO }]'<.nl!1^%y+ 9a(,{i Hs4x7*ԣ\?RNKb&5no.ڐ[s$hA"UgI7]3tA{Ihg5xk=:* 'Xݛ^o~(G۔TT@/  ƴ((k^7T5"4}8(Sz *nV~R؅h: .xjR=h KLz>u/#>,'$ 'L22?gs,1JY=~wN)-ѼR =!iWT[+#;#3g#'e.\efwDH3itĮ.N/.28fK.1Pu<= ݄c#cxw{}}_L=kyl-HR~?l7360#v휞1u*E:[lD;g@@_e< T#ά"[:/&=rnqMxT5NuY^UN;v"rG\\+:r\sɶep x/^}=uꄄ&*Ӟ@Ѡ1Jok-.Y!N#$o M7R#0)9 "L[ȱ GrT=E"=o79sX9'gWah1>Eu<t`/-)+~?Z̒!f-8C}qmLӣ4f/:hl%h@ j4ڏQ9N)ȯFBeW[C8,Z 緻zK$$*T`.}&^[<=\WAvN) $ }6HV^ZjQoD2YMQıG7hWAoRXr Y£na7Yf*>) ]`>@jRNpD_h]V/u`GA}_ G7ݱiRU7ieG>E9[)'ofG&%'ŏ4PLB$B_JMSnwR 73pMRp%'qŠs&D ƃc"" hzz:1rzʣsD`dcBk.9T)^WLBI$(U(|C&JHUfR=?,\ա na®lm0Dw _z;x^5C(]xV \"LH,yKJk`Zf&srK(O2MqR~}Eۘ]4j%ׂHO 9ga7"Bp3ߞ<\El+]bïdrXSÉK 2o;|02!65nkeMË7ѝ>>^^(b$RdMhls.ƔVWݩ uXXkSS^W09H@7RF W9qO:QBd옘*sc7?ARVLۀwM>tHyRLEn{mT= 9ճLX!׫biĖ [.VV*O~(p0on*<;.TS`U=uUITn,+ݓe?=\vx!(g):b`]Q Z֕#>TTq,QmXAc|{ƯbG&ԁp2ϟq+_ZޞmW(9:F9@Dk^]{O%Ԡe_XzRq0 5ʻD=rIұ1 o-A{Qn?Əܘ`H Uicz r  H-gzSx'u%Wnr.=EGՑ$@`ۢxL8\gi~'Ɍ,|m~Lk-Bf9Ȍ#iS_<h {|3 @0]+kzVr~WuR~ORB p&3D#z )Bwb_ft5 .w5V"&k).87|^!~d7L@};R?)n|UV1ǰe!vxbg#ōkEemЅBlv`xA**`&KwU8}-5.P{'0Sb>;i}6O\~`?SZz??94.]~^x@)x?®ݍ^ߦZ£!rZE[t@ F8> `NiU:2f]U y_D19{%YwY0OMnλ#eADkhFC{^N Մȵ&ՀYK} ḧsJ@wxG_&f]w[¸+oBeׄf v!83~tˏzpq1jd54\Ҙ>- x@nqQV:1")軭NhN73[L m“rb:6DAKfd^C;;IU]5R"!?Iʴj:P~(G#=?prD$)K'>lA?ϔ55:~fݭf{s@e?݄&[J6]l8(]6%8'6#^N3ۗ7HdWoIcoQ3PU4~{\ ލM5ԯQRchG^=7'KW6*{.E!@B\EBF#Q M}`ͣeج:7Nq"L0o(6!p99^[Ͻ|zj>i Wё6/_3EÁ=(@`!=z =DA :]AewSpf%֔ߩl;:+:8ђg g)hg@OnLfLRfr߸PB^Hs GyLH7=ܹg9N2$ZQ$sm6+-70zS0l, DȘ)aL \Ro:>TNO,~mU80躟+U&? L?̴qpu&UN~EZ#@;FӸ+t[qEL:m9{PxЈwf4|bFk|ZɒM?;.E6w"1?L ^72~GPFnvMFwaG nFߞ!\t:N7vszl@8iQ3<,dpFYрQyaLN.JA:9"e%* E8]} /\R3qCr=A!}6Y!Bк ZܸYS8*F#b0Zl9й,SɒHa3Ok@7l>2w=l9+EKAXCB J!Y8I𼇾f ٟ,:LiKM{wΠCsԇ`UΉ-︦%Rk~,hK Iql ƟBvw1~?E*B…:F vr=#8Ӥ )#^BQBW}C )䶯9kuN@ʺf>WmC*k 'b y]Mwܧj &˼j0/4"aٓzAQü7z/Ή~Y0H#h3*UgC47婸nxOW+:xYۨI DBKG }oVbWds3 &A}}s sdx|pRbáPښy I'g]v<ށ6SU&exsA/o\ݸi಑',ٺ}㐒d':SOm<^P565L0(~S@s\svR?>j$ %`KmMb'l%V=@!Xcz5VƯʧ& AYU s"<]^`4 t*mEDKџ?ӑ+2Q{DIӫ O!F;xEM!b޽8R8|,IIyty^+CZ=0H Y|d?ٴŸ& +`-Gp9'OI Bi =a]zefF4p2/֢ Y k0v!*Dgr| h~!PJv^䫴\ָo&a$ԱK<%.S߮BhQ#T+ >37Q` zV$um1[7T('hQrZwO_l2b)P2tDX~Ir8lHHF.DK^; G;=mi"16t1_IG%k][>jg"׺e:5RTf߬Qj9<܌OR!>SqWNި$_pVMEc6bwށ";/_Nnf5*RtLi{yC_Uѩ }?H?KdluNgphzc@T~ICTkx8ma,ߑp]-mqٵ8$AO*Oe٪.vpYV%x(^WYv?s 6L(><pccND^#Ytໆ;xM wy쮚HkaP:}|X׈@ $Y^CwS_!~,NzPZroTʫ̨2 Q`i3P5C ^lŠyoe` WUͫf[Ǻs.2ewl4/݈6,%ikU-5 j k'?ǭ73@X?sݭn ?W~o4bgLla1+!0|4n2l1r#YSY5v:}>$uUBhKK\䂰W?av@Ւ΀BxGa̰<1AƄkYGN) s|2T;@!,@Wt0UoVX]Xzj.Y(ױ}/˔TJ4!:D8)V ]/MiK!%LY @ j3ހ$1t1Sz/_T̾(Rc:}uUL)e_U1/n6$QOp/wo'%犩~ BBZN28;N#@K5yfWbe=Mn2/Mc9)ϻSh !9+ǠBbysMQ+&,߂f}0rլkvR<d1<9T(pҠx33lDPdƹC)q{/Axڄ N 'W,<:]{G=>K(>؊r937ML$Zȟn=.zCweqE8N%4ɥGvG$#'^O-PK]j A"bX,o/Y5RwUxz(̆`t}C2=OjIs\yQ{w|e._p|"^<SQS\ Cn4#?T]&@AwK,tkŧ6k'N4%t'`}l&u;2jKKSruYYVH@cag{r>l6%mc`-A064 TRݔuY>vKCT螟 Hsk.ms+M8|d9C_Đ(M*!gŚ)|^'il* ;x51 ,a'ye0vgb͐x;P}OH-ClNȀTŊ^ 7rJ9YfsSnqFvtiՖI8CjY|d8+4Jx|Eqb3<f0j(ƻoLyZϨUzsqj!FDz~psz- HJM3?KVv茟\f=0 E4N&URW}0 C#/n?\mJ)YEA/-N .n7\PANc k*6;U|R{# cS>l>qwi((0j4ny"XuB3%euuz5?8/I&@[2/6hU >K4BH| MH3o~|iCtU@I59h*وYp@Þyz>{G}u|C !27xT)<_ht-trXur`d?uJ#@\B G5ҭ$R޼ǽ]Lkl G7 5 `&psʵߥK#j,T}R{I=lf_z bTX{`ו+Ӊ.H3/e%|Fyh +!|3Rⲅ 0pB@v"Esd :P۽Ěp7p* GQ@YGK,M$vɼ/ߐ&#; M [ gl@̀ULFFCʧ\RcaG;(/Zq$CՆa=C6 xN(TEtf,;.<~l'' .Y ނC1tn\ VB MFʗ qVp]@MqB|R9~*sһHptqH-aeݕěEHBphef zčCt:r.)QᩍcM.yx[WcM[74Ke#݉1H9L2ZTLL!Kay,X4oLKxXUOk>!A Z}`&"bY c+U1WiKUͽ4ma|K_8ajy2zhszQ[˪$ݣcnpcV ^\ 7Վ}zR ʝ+Q׬MWY}x#MSQForX3+{_$8 ⨽aχB?/$06Ƴzlp##ԟm4B^=\ <+^p'HCtdh p1G}Jw MyBLY!?,<$Bކ )D_8Pôpiwd㯢9I~)/ .?ܾta6b<.LT1qȉ7"v=L!EwU~7 j<ΒLL$%D[ZN;nl^cy0[p~y+oJZ{3KN x䯰> McC}2l";'oVBL8tR}\S`TLsl@o.)s{#*B4etI3Eb Nsti<Q5=)I*D0tcGiL|OUH/ԇ$/2[Fq7`kk)Oȫ^ (/l`OR dB –J9׽ Oa!F^<(xM DqM%-6yz1hR.O7`SEDජFgGdW U7B"W2O`KFvu겘UMS|ëW <>!9L ȿ3DIh#CI=fX_0/5ί&FAE-sBvMBϳ$͊շ2oÎZX|Ȅ@>h2j30BKjp49A*0JEGG`:>ckxѶ~;qbhB4co0o ϧ.H8T3!ڛgŒ뱹BWT4O 7+NcCG;ͣ9Còo'tإ+s9d J|X+{%?T \5bSЌ3GYnb:Kn ;P%S`_)0Rp6}JWaS# 9œtF &֦o]wvB,' +5*y: LEX';SGY {OɃZG5S&D*W[LLhH2Xri Qpqɶ`֗e&>P)P߻L2dm/{Z yޠi|H]Vy-N6hP)we40`|%|!Ҹ9noGrLOٺ{;׶t"Yjsz~ &7.ҊJ@-u\/̛Z~4RH%"λSʇ ir|pϝ>Ws+cwM)$˶8,X_=Y~BوLhbiP[o\n_y貧uumed2`B8ŻS¸C#⌳yWрPsX28-֊rs|2сGH* w~Jꛯf(?Ba\!U_{%z0CYzpiCk[Y5o4Z4Pl[ y{X_9ǓWwZ^п49Cp~WPwLGrE20~ V} %@-8ݜPXhj6İq/s HhhMJbob_|; Ϣ,dRCa>u*!$eR>Əo^u`9cZxM59j\䥎#W׆R/iߘR<*\0 nxSʣ9POx}>dNSRSp-yixJI( ,ϭe\j?KՖ_sމ#/88a'{(FSi|V {Rmr :p5\npP DXebRgh=Iwk ns{} ‹!⦯z< bI:Ǎ{;FlۋU1|ц0V}T |1vɤ[Q1s.RC EiMg ĢNB߭EcNI/m[z,'A8!e³  02^7cwx-_U+P 2A))w9> r-CsH [ T$)|ge,wٜmKmY!$.eCVc C9e#=O+#Ǿ94'#*Yų#)sZxxPG4< Qgz#|+ vp0c㈌uyOZL쌔j.EBHAQc"Ób8 ?a^UrD=‰ `k5b>L*U[׵"1^Q q,VF _;*6yPcIpvqt7᛿q 1 %d/*[M$=>e7KXbZ:?͌H˫a: \[`v1geak~ ksG籩Op̀)b/O?xuFG0<'VFp\Sg Z N?{pCۃ/LqA2U' ϩύTZ 6 *}? W@{He:D_lXZY`ejm4^rxOs\IYa--DǏFִa*fj;O֖28(H2f[#4WFHVk-g^.ewP*_a^Φ-Lz'jGw)a;_:eL:[2%M՚ݡS"k&5:_1\81 _rN VMo$~,Bl4[ q+^/и_ XB!382&{T!sLvWHۊE y"좣tnD"B/}hsnE˺_ӥ F fD# 8#gIsɼN2L!Uw:w}a jՈy"umaR+SS@Op/V\:TH eWT?EdN UL3{&JVIЕIRm On bq/@ոm|9Oc@Vdm^\w m^\w0m^\w@m^\wPm^\w`m^\wpm^\w@5 c1bEޡ8a8d TAP KM Ө: rmߌxIV)5$7@ˤ-*]-VZ7iHS!9ny %_]ы] "ڠ2U|61[ϼ"Ӌ3dH @s/Dz[s zDaCk(t0Z$A7,{؀m>!(`MR,leج@iHR4p~>D WYHg>rr8m(~ko~ g:PT𥑺iZ(EJnD\ S}H[!`-%@+8T;HIzbU,k,rj Ʃ(;pu";6)i;@|]Urt<&hBx p'q h=f R@>K>h ]bLN@Dt馅PCL}Wo/w!# W };Gȭ'XO(mpҩ4SN%:G>@ ͆5DՉ!r1;NWb R2a3nZQT`TUoM :,y^JG!t'YDj@zӖREJWÚkE T@ ؄{ -E)(lG Ү7<4D`o_w ?JqbFIu5Mu <1@ "h[f3dIh 'B +n3SG1-9@F(>O=`|.<0|`H(ū-w2\iqsz*@ kOA'ֽpzas4þ ڧ½ GDG!yTJ*(Bf5As'TCԪvp0U wzM+Hȹ*W#ze@'`z܏/( Ӣh]eϯs p9)7jn b%1(65l(|˹oP*F*.f>!)4L>$Zu<6L5ɎTY#Ep]lxzɠұU@UD!-vӒ~ǗC'BE)y?<]@ 1X.+v ;"lb(jLL^Bzg_X@? Zk=1;Yú,:xزO0O,<<lOhKiU+ >S˺beT#-1#HQ>.md%̳m {W!mƞn@ӖƟMȭEVw_J"~)<i3u4$m'I8~},bu3v'@dzK|}L6lZ(U^ .?XAfg(6woUr/q7hs=] !E4t5*0X^FOh.5`B%vWS4TbI@c%JC,ٓ2(76+ oU16HzɎȴZ^wFF&QAfmK^CKm~c0_iTݪY"5.]B ¨HѪ<- 71FƼ@[ME 9/l.e,#l6ݠ +&ي"%^M#Cʿ7b3g$L;XX9(a>51b/GTOT`ͬ+i7H3W.XaD@:vc|H~^+@xGp^\h2 D-s>b[dRXMtM;^;Lއsl7#9)V 35^֚ -AO9MJ>{=g(30@ ?>z*bؖU TxT+xt .>'Gi)eOtY.Zz*DX}%©ObݿJbt%l#/?cbͮ.Gr5n .3{\%w؍= )FF0@iK4bYs/;"CT\]vϑyS5l̥, Qa7DJ@TC!@Ln>NI6Hj9clIlz[,}A/䧈YO9uˡ=<2%tN.7Oe1-/|ɖ`|,*]s%t2Ьh`sM@mP enA䚅V{`/d[{K |>/skM ƤcXygjɱRZ$nktxj^~T@Dw{͍e9.@3$y#L2fY}\@ `+\k~/cVB=J]7 uw͢ji~gmAX8 +x[np %' -bKkA8hkB4l.+N'[XKe+߸8_ 8쇨$֛E^y6$5.*ؕe|Ԝ@V4=EC~C8lƇ\"O iy^RVnf\4EF`AKgo!~5:#el5^)"]yaK>RҒ?zYJLIp/ 䆱9x0MHtWz#s{ XC{$6ۮob"5j<(p)iy}@:hR4힧tHb(rkܼ c H[1ejP#:7A|ю~ZjzAsJ^(:q9D+9 H Dü,k+%;}+b[/ 'hA7{$NS@GoPQߦleӲX[1A1 ͰXj JLxDKY(SNC[nJ95lY>}BXC+ݳ{傩2aE!y]&LJT:_oi/t.4˾9!߂>ɐYG9r] (,+ ӑ*QDu"6v%)? SHv7uJu#b/)~r}q.BROmV^E b X|qOlCA@H)1WGsk,lQ5-mo&!q׈c":Xj5IMe) K#Dt#s\ q!+gE&"7vy:_W*<뙗>fSquhyM#4Nk`"]Bu =+^ 1j!dSn fN!/]a5DW]{ f^oڍwJccm|vk*|5B(]"p&U%NCS!WXtmU}dz渠-[&cSs a傕eNL)|~gf24n 䒋(J塗(B!lSxYWfMkʪ^Cn'- 75X<8uǬD)^^9=b[{:pt#>aNݘqv[t0wvp=hֆ!蒢aPnQk۞Z5{MLl WE Oa}R!wp$yTU|\ڈܓ\`a>d\g@Cvmv{P9|&`n?}x-}CQtf Kgsk:ʅ>PSEaRgX^NhoaX-xmu|vi#$Q,y!uGLC[Hk._eSA8+b\U깦W]D_Y8Fnhq[?"r7͸%=/>"%Fg4$xg$:vƝ8}C')ѰשRʠ|~$3vTCZsi~`cP裸'ߺ>)&I4Ґb&3 ŘN݁h`4E#)tUo=|Q|7˻@M 1nv-]/-iFjQhb8W?fLcNu[ֽiڮ{eH{H\6Xsg`0_;an"C៍d㖲5*"WSῧvGuG M==30%(dUM'4  % nAl~D7$yw0Kf\Q 1c-HbkHj*̉Jg6E"}L`OL-\"Dch 9Y&σ+ߝ]ґ,k/5Nf]zr^N09LDy|̡nTۗg89ȘҨa$Q _GD%i?Y(Kb/E砀 rjLo\uIp104"7.\gQ̾@=X@k{ACI֥H iGXմC+k羛wT% ];^O&SU kpQ|0l[M=>yދ)sM,ja2oxH/iV]KдI9|RH{r/;@~W(9fEL,}BD9bZ{EWy .z":"ij-"- Ca '󑜱܁xR &3H~ݑ>~?@&+]h.od; Bs֊KG>3?It&D7В9;?^*OeW+P/0݆J@a2pU"!z?qW]]BA$ЬF,J/߬ _|9HOxP0d1^} A a@*Y4@ I<.[moXh Csnw1uL:o aP^@;oJcX<)P-y/ٛ:6/Z3rq)Ԛ&دwU =Q^X \\ZtRw':+objIH@eW#{OR$^@p%&?V31-#_TFJyc{mEqh]@ -pR3y'K&«녍i4K(0EkCO)KM(} yAƖ(t3rR)~pcK }AD-'ڟ3VOX ^H!$4P~k( 6^s3 vG~^*M.o#뫄BL=c&'5;Q&uJ('7pymswt~b.4 Jܯ'jMY{# }M` Д̳3\L6B~F슭zVyìAA)w:K!5&ٸK_1 ilMY:V?ŏy(X0ŭј\n|G D!PiyjsJF|lu+ܩ# `< z Dʳ{Zԯ%T![Ԡ3:׾Ԅpap'ϭu^SBfG4c @~ q+;Dti~ AdD Q9z|V}asvuQmH>AƢEeFx".vBv<ڟ'F$N&u,A~JW$|.-jN,oznYQ9M0fH߽cl>rhU_F#hg ~y4^^b&v.($ 6_c\z.Pԟt6tW_OEgg|)[zs ئht\Ǿ |tւNda=c6^ܘA﫥9`JtFmr .%dT}$h vG/>z#ao"N7&bqJ xh> fI8VQۗFC0B:e ةa1 P:rλTe%4(c=NݹζhJXCr.}6zIقm`]5Nkgv`5׾?cU2r)=65X.2 *X=vz<yq<ΦDRS4;+a^;egەлac3yMPZlNs_3pc4KIoCMX)ae= -`W<@SZIͰoq7g,# (2~R:"Sֱ74ygÝP7~~GaPemHm6eOǛfR_9APO34|E1i:`Qj^P)x]Ò{?J|̠WhѠˑ|7-Jh'oGR]N{#\Mdm,26.Q܈6s'-۾&sٴ"xfМZyEeO1kZᵦp|{[ dnĮCM`]N!JܘDQ=tI,+yv ykl1hHp\eRGɇ1ۖO&:ݟBa[mkjyV;OuuQlN /ye04$ ʹ$aLOcFN 3UHނF(5bHG{Y\Rfc/l'>1jms+YLHr>!Ƭs "GyO2] T%+d-KAtXfb~Oo`i e}<i:C7^`;`cð0tCa:i@¡/Q1F¿t[kWg"k{{ؿ[biC@ҽ 4ԫ8=X!dQoo:\*p)ج,0T%Kg1+ 39J:/dƼoXzeհ]ڳ`NvQfx ܣIC ֏'fzyLi]Wٳ]NsAoڔ1 ~ fnvH6')>!̵/F\HU]I*DJe c O |]T4K+0t[ɸ7sq&ZD`OMPID.3U<&aGgԟS`?"iqeXN9DX|Z[,l$N*t(:dҰ$?42iI" ^35Ⱦ=7zrACqGӕIvqL|/}4Nփf΂O*W.0E5v,k6jD~'z!(YwDܿT CX;:㏨Sfh>|5^qs3 b=eF2 ?.KJd#QW@50+ʅ-%l i.x|8}T.M@zt$&*:ҁp<bдeR^ZIg6%B lҡU=i3vo($)i*czbS8L$ua8/);:K[`gC3UG(u0z3?fb΍cJIK*% ˱*:3E/x7~gR?kKR;soRh!Ӵa؏\y%6!BTEKTlVr~E~3 bNfv[6Fp3bㆥ tŀ,>X7(Y# )NVwA=T J#6Hn4dUTMw-"s&R>l$8̦` K;@cSC\p)[OV@T <]@ZkyGE Q][p:5V )e h*N 9܏WبE QIr&v(Mz椢k!KՅ>. O2%J<:$C@1Z00Ѳo1Bb)@ja>to4>,s_wJ>H XܵC#A{E<эܠغ Xs̯i>2\C"S)sc!GcQ8#+Y47Jm0< ;;.q&YxDEЏ쌛: ђ7 Q4g!{³ew"mB Fth%.tp\")ZX*CZ˦WHBӲUutC& .SG;϶4n[મC/C#LT`OHnl;f\ :[3Ƅd$K b1[@Z֔[铗 $e[Å_YLnю?l"Pw_#EpTeq6Sfcޤla>ϚR:uXCbW+75z0!sKy謱ټxNoE`ۊrZ*)KΨ8pP sSTȷQ2.g`Li<=fcߖ;Uk|_+#' /Xf 3&Az&E]$ߓARC\_fzME\ j0GuDcu;J#8q鵣3mM6~"WCQlwC6~d?W B!ÎoG#9/ t< lgJSg^ n6}ݕ:W+Q"/Cng4b矩a+40f܋7|߫9ño H2Ӱ}[kaI\9U9;tmo`fYfc]ɊnJ7&?E;TH `d }-5/V:]fk[.~jnKT,_dF6U /ApM!:<HÁ1*J3qI$W6ᤌ~N`QgÝB?{XjJg#y&}'^JQH ] R1uXrFHӔXj@MQF'Zh/Ȭt/n9FGH(OpGJj>50*`*Yep5{ ͪDo-V(N ydiͷ9@nm_Ƙ l@-z6Y|{Ԙ?(Cxw(/Ɍ|yI;Uݲ6Ƚv'`oRJD)o`qнP^QEfa["aadvG39+>ĕUDMlUX4|sSqzڦEm8/٠jII;oTY8x14K jxb:;i#Ǝ:R ScP7d m`D9Zp?%w/:ZB6'Xy/i!>L&rSyf{a]aadžڣ <#t7/B!oW\s*e[#8L:m_ ު8޷ Q`]`fuAu Q2ޔ"l0o&KYE 6!z\w7%"!ضbz|Wj'o$F O8wɟ-w:(hoµzNnቶ)r9ƹ-֤mt?"Yi%",~f/[F%8]n>-D,M땮VTLwq `gPP{M.%w1?;iʗw"L܉/=.3(618m(Ax5$dp4o2@ ^#/(s{X1c/^g%o@JQ^|h6Z>F!G( F٩ D&͋z8=GG 9ӎVoX+BOvҁz:24\t/P~NU,UJs˘#,T҈BuefWk qT=Ud.ޯSxUhVe~9/ lJ6<|84?$wݎpL${l$F'^vL͋^|nkyW-!ބPzW *ľ 1?;֌zFI#ƭV^۷ XV%<_q<݇Md27WgNN[$q8h. a.iiyF"*` ^ŅK/ŷa$ ~9i Ns:nAVLUSIvV6~&3˽sb*tp_kM \⛶;4q݂#2.fՓc;Q>F[m'ZE + IJ\}̴EW/0w^['b>]c]mJ2qhȏl c/ G{ç #6HcNi9DlGX؍%)U5V9\&)Io0 sۇ9`mmVMTY 򷏧^k #NC7wJInEv&#tz3I[͐`rt0h\iʢDw.˼ HrȨ#cH Jy%/l+0@̈́-ibT?Jŷȍ q%>!7ln4{grf㘑rw?H~T!+U -5^_I[4 ny$/lA*~ "v]!hᡩ{y 7nh2;;y2`3"oLYLj/_X2M/= [C_}Q8^ޱ5RAHN+:h "]^SV=+h(ּ>(-P-:c^iwz-(*@qu?Ix[\Avd-nGn17)\kkbpG '—3ރgGVL}Gz7*3B`XԄ>Udܕj;06)Q8)S}EZ%ܪk^dkao ֨vrԱϾRז.RO8MC5&b*ꀫauVwVX^?PMH */yYà[،A/`PJ HRRg\D@ 2_8OZsͯfO;TJ|3m(raQ?2{5ƫ+n$_Vpݱ mCᱣq+[^qV7=, ڑ;8 3 <'*DVQԍ͈{yr?|a}aAM_+[ E+zՠ냊5EY9cl8N|$X/ } aE^kӜ'97aLd?(+$fG0#&mG++dV <;|'f2¥jxH$d,t},P3B;fe2ܾjx U o{EBkx'ŒL^[pvǒ`bljfkᦹq6G9Q$aktT{|=ص!L;Ӓ_l]ŔH>1 eHITle\ v8==&:\nX%rGu0U!~wG2e(A9CkPEViF:Ob"h/h,GIB:τqJvNÃK THt/W,Vl3E:CGQwi.2I-ZŕeLxgnmj0VG=_j34B$ZC.#1GRDVV<  /hX~;K6٦9M& jն_eҼ -3[ { ikm: &'a [u8], salt: &'a [u8], info: &'a [u8], prk: &'a [u8], okm: &'a [u8], } // Test Vectors from https://tools.ietf.org/html/rfc5869. #[test] #[rustfmt::skip] fn test_rfc5869_sha256() { let tests = [ Test { // Test Case 1 ikm: &hex!("0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b"), salt: &hex!("000102030405060708090a0b0c"), info: &hex!("f0f1f2f3f4f5f6f7f8f9"), prk: &hex!(" 077709362c2e32df0ddc3f0dc47bba63 90b6c73bb50f9c3122ec844ad7c2b3e5 "), okm: &hex!(" 3cb25f25faacd57a90434f64d0362f2a 2d2d0a90cf1a5a4c5db02d56ecc4c5bf 34007208d5b887185865 "), }, Test { // Test Case 2 ikm: &hex!(" 000102030405060708090a0b0c0d0e0f 101112131415161718191a1b1c1d1e1f 202122232425262728292a2b2c2d2e2f 303132333435363738393a3b3c3d3e3f 404142434445464748494a4b4c4d4e4f "), salt: &hex!(" 606162636465666768696a6b6c6d6e6f 707172737475767778797a7b7c7d7e7f 808182838485868788898a8b8c8d8e8f 909192939495969798999a9b9c9d9e9f a0a1a2a3a4a5a6a7a8a9aaabacadaeaf "), info: &hex!(" b0b1b2b3b4b5b6b7b8b9babbbcbdbebf c0c1c2c3c4c5c6c7c8c9cacbcccdcecf d0d1d2d3d4d5d6d7d8d9dadbdcdddedf e0e1e2e3e4e5e6e7e8e9eaebecedeeef f0f1f2f3f4f5f6f7f8f9fafbfcfdfeff "), prk: &hex!(" 06a6b88c5853361a06104c9ceb35b45c ef760014904671014a193f40c15fc244 "), okm: &hex!(" b11e398dc80327a1c8e7f78c596a4934 4f012eda2d4efad8a050cc4c19afa97c 59045a99cac7827271cb41c65e590e09 da3275600c2f09b8367793a9aca3db71 cc30c58179ec3e87c14c01d5c1f3434f 1d87 "), }, Test { // Test Case 3 ikm: &hex!("0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b"), salt: &hex!(""), info: &hex!(""), prk: &hex!(" 19ef24a32c717b167f33a91d6f648bdf 96596776afdb6377ac434c1c293ccb04 "), okm: &hex!(" 8da4e775a563c18f715f802a063c5a31 b8a11f5c5ee1879ec3454e5f3c738d2d 9d201395faa4b61a96c8 "), }, ]; for Test { ikm, salt, info, prk, okm } in tests.iter() { let salt = if salt.is_empty() { None } else { Some(&salt[..]) }; let (prk2, hkdf) = Hkdf::::extract(salt, ikm); let mut okm2 = vec![0u8; okm.len()]; assert!(hkdf.expand(&info[..], &mut okm2).is_ok()); assert_eq!(prk2[..], prk[..]); assert_eq!(okm2[..], okm[..]); okm2.iter_mut().for_each(|b| *b = 0); let hkdf = Hkdf::::from_prk(prk).unwrap(); assert!(hkdf.expand(&info[..], &mut okm2).is_ok()); assert_eq!(okm2[..], okm[..]); } } #[test] #[rustfmt::skip] fn test_rfc5869_sha1() { let tests = [ Test { // Test Case 4 ikm: &hex!("0b0b0b0b0b0b0b0b0b0b0b"), salt: &hex!("000102030405060708090a0b0c"), info: &hex!("f0f1f2f3f4f5f6f7f8f9"), prk: &hex!("9b6c18c432a7bf8f0e71c8eb88f4b30baa2ba243"), okm: &hex!(" 085a01ea1b10f36933068b56efa5ad81 a4f14b822f5b091568a9cdd4f155fda2 c22e422478d305f3f896 "), }, Test { // Test Case 5 ikm: &hex!(" 000102030405060708090a0b0c0d0e0f 101112131415161718191a1b1c1d1e1f 202122232425262728292a2b2c2d2e2f 303132333435363738393a3b3c3d3e3f 404142434445464748494a4b4c4d4e4f "), salt: &hex!(" 606162636465666768696a6b6c6d6e6f 707172737475767778797a7b7c7d7e7f 808182838485868788898a8b8c8d8e8f 909192939495969798999a9b9c9d9e9f a0a1a2a3a4a5a6a7a8a9aaabacadaeaf "), info: &hex!(" b0b1b2b3b4b5b6b7b8b9babbbcbdbebf c0c1c2c3c4c5c6c7c8c9cacbcccdcecf d0d1d2d3d4d5d6d7d8d9dadbdcdddedf e0e1e2e3e4e5e6e7e8e9eaebecedeeef f0f1f2f3f4f5f6f7f8f9fafbfcfdfeff "), prk: &hex!("8adae09a2a307059478d309b26c4115a224cfaf6"), okm: &hex!(" 0bd770a74d1160f7c9f12cd5912a06eb ff6adcae899d92191fe4305673ba2ffe 8fa3f1a4e5ad79f3f334b3b202b2173c 486ea37ce3d397ed034c7f9dfeb15c5e 927336d0441f4c4300e2cff0d0900b52 d3b4 "), }, Test { // Test Case 6 ikm: &hex!("0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b"), salt: &hex!(""), info: &hex!(""), prk: &hex!("da8c8a73c7fa77288ec6f5e7c297786aa0d32d01"), okm: &hex!(" 0ac1af7002b3d761d1e55298da9d0506 b9ae52057220a306e07b6b87e8df21d0 ea00033de03984d34918 "), }, Test { // Test Case 7 ikm: &hex!("0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c"), salt: &hex!(""), // "Not Provided" info: &hex!(""), prk: &hex!("2adccada18779e7c2077ad2eb19d3f3e731385dd"), okm: &hex!(" 2c91117204d745f3500d636a62f64f0a b3bae548aa53d423b0d1f27ebba6f5e5 673a081d70cce7acfc48 "), }, ]; for Test { ikm, salt, info, prk, okm } in tests.iter() { let salt = if salt.is_empty() { None } else { Some(&salt[..]) }; let (prk2, hkdf) = Hkdf::::extract(salt, ikm); let mut okm2 = vec![0u8; okm.len()]; assert!(hkdf.expand(&info[..], &mut okm2).is_ok()); assert_eq!(prk2[..], prk[..]); assert_eq!(okm2[..], okm[..]); okm2.iter_mut().for_each(|b| *b = 0); let hkdf = Hkdf::::from_prk(prk).unwrap(); assert!(hkdf.expand(&info[..], &mut okm2).is_ok()); assert_eq!(okm2[..], okm[..]); } } const MAX_SHA256_LENGTH: usize = 255 * (256 / 8); // =8160 #[test] fn test_lengths() { let hkdf = Hkdf::::new(None, &[]); let mut longest = vec![0u8; MAX_SHA256_LENGTH]; assert!(hkdf.expand(&[], &mut longest).is_ok()); // Runtime is O(length), so exhaustively testing all legal lengths // would take too long (at least without --release). Only test a // subset: the first 500, the last 10, and every 100th in between. let range = 500..MAX_SHA256_LENGTH - 10; let lengths = (0..MAX_SHA256_LENGTH + 1).filter(|len| !range.contains(len) || *len % 100 == 0); for length in lengths { let mut okm = vec![0u8; length]; assert!(hkdf.expand(&[], &mut okm).is_ok()); assert_eq!(okm.len(), length); assert_eq!(okm[..], longest[..length]); } } #[test] fn test_max_length() { let hkdf = Hkdf::::new(Some(&[]), &[]); let mut okm = vec![0u8; MAX_SHA256_LENGTH]; assert!(hkdf.expand(&[], &mut okm).is_ok()); } #[test] fn test_max_length_exceeded() { let hkdf = Hkdf::::new(Some(&[]), &[]); let mut okm = vec![0u8; MAX_SHA256_LENGTH + 1]; assert!(hkdf.expand(&[], &mut okm).is_err()); } #[test] fn test_unsupported_length() { let hkdf = Hkdf::::new(Some(&[]), &[]); let mut okm = vec![0u8; 90000]; assert!(hkdf.expand(&[], &mut okm).is_err()); } #[test] fn test_prk_too_short() { use sha2::digest::Digest; let output_len = Sha256::output_size(); let prk = vec![0; output_len - 1]; assert!(Hkdf::::from_prk(&prk).is_err()); } #[test] #[rustfmt::skip] fn test_derive_sha1_with_none() { let ikm = hex!("0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c"); let salt = None; let info = hex!(""); let (prk, hkdf) = Hkdf::::extract(salt, &ikm[..]); let mut okm = [0u8; 42]; assert!(hkdf.expand(&info[..], &mut okm).is_ok()); assert_eq!( prk[..], hex!("2adccada18779e7c2077ad2eb19d3f3e731385dd")[..] ); assert_eq!( okm[..], hex!(" 2c91117204d745f3500d636a62f64f0a b3bae548aa53d423b0d1f27ebba6f5e5 673a081d70cce7acfc48 ")[..], ); } #[test] fn test_expand_multi_info() { let info_components = &[ &b"09090909090909090909090909090909090909090909"[..], &b"8a8a8a8a8a8a8a8a8a8a8a8a8a8a8a8a8a8a8a8a8a"[..], &b"0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0"[..], &b"4c4c4c4c4c4c4c4c4c4c4c4c4c4c4c4c4c4c4"[..], &b"1d1d1d1d1d1d1d1d1d1d1d1d1d1d1d1d1d"[..], ]; let (_, hkdf_ctx) = Hkdf::::extract(None, b"some ikm here"); // Compute HKDF-Expand on the concatenation of all the info components let mut oneshot_res = [0u8; 16]; hkdf_ctx .expand(&info_components.concat(), &mut oneshot_res) .unwrap(); // Now iteratively join the components of info_components until it's all 1 component. The value // of HKDF-Expand should be the same throughout let mut num_concatted = 0; let mut info_head = Vec::new(); while num_concatted < info_components.len() { info_head.extend(info_components[num_concatted]); // Build the new input to be the info head followed by the remaining components let input: Vec<&[u8]> = iter::once(info_head.as_slice()) .chain(info_components.iter().cloned().skip(num_concatted + 1)) .collect(); // Compute and compare to the one-shot answer let mut multipart_res = [0u8; 16]; hkdf_ctx .expand_multi_info(&input, &mut multipart_res) .unwrap(); assert_eq!(multipart_res, oneshot_res); num_concatted += 1; } } #[test] fn test_extract_streaming() { let ikm_components = &[ &b"09090909090909090909090909090909090909090909"[..], &b"8a8a8a8a8a8a8a8a8a8a8a8a8a8a8a8a8a8a8a8a8a"[..], &b"0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0"[..], &b"4c4c4c4c4c4c4c4c4c4c4c4c4c4c4c4c4c4c4"[..], &b"1d1d1d1d1d1d1d1d1d1d1d1d1d1d1d1d1d"[..], ]; let salt = b"mysalt"; // Compute HKDF-Extract on the concatenation of all the IKM components let (oneshot_res, _) = Hkdf::::extract(Some(&salt[..]), &ikm_components.concat()); // Now iteratively join the components of ikm_components until it's all 1 component. The value // of HKDF-Extract should be the same throughout let mut num_concatted = 0; let mut ikm_head = Vec::new(); while num_concatted < ikm_components.len() { ikm_head.extend(ikm_components[num_concatted]); // Make a new extraction context and build the new input to be the IKM head followed by the // remaining components let mut extract_ctx = HkdfExtract::::new(Some(&salt[..])); let input = iter::once(ikm_head.as_slice()) .chain(ikm_components.iter().cloned().skip(num_concatted + 1)); // Stream in the IKM input in the chunks specified for ikm in input { extract_ctx.input_ikm(ikm); } // Finalize and compare to the one-shot answer let (multipart_res, _) = extract_ctx.finalize(); assert_eq!(multipart_res, oneshot_res); num_concatted += 1; } let mut num_concatted = 0; let mut ikm_head = Vec::new(); while num_concatted < ikm_components.len() { ikm_head.extend(ikm_components[num_concatted]); // Make a new extraction context and build the new input to be the IKM head followed by the // remaining components let mut extract_ctx = SimpleHkdfExtract::::new(Some(&salt[..])); let input = iter::once(ikm_head.as_slice()) .chain(ikm_components.iter().cloned().skip(num_concatted + 1)); // Stream in the IKM input in the chunks specified for ikm in input { extract_ctx.input_ikm(ikm); } // Finalize and compare to the one-shot answer let (multipart_res, _) = extract_ctx.finalize(); assert_eq!(multipart_res, oneshot_res); num_concatted += 1; } } /// Define test macro_rules! new_test { ($name:ident, $test_name:expr, $hkdf:ty) => { #[test] fn $name() { use blobby::Blob4Iterator; fn run_test(ikm: &[u8], salt: &[u8], info: &[u8], okm: &[u8]) -> Option<&'static str> { let prk = <$hkdf>::new(Some(salt), ikm); let mut got_okm = vec![0; okm.len()]; if prk.expand(info, &mut got_okm).is_err() { return Some("prk expand"); } if got_okm != okm { return Some("mismatch in okm"); } None } let data = include_bytes!(concat!("data/", $test_name, ".blb")); for (i, row) in Blob4Iterator::new(data).unwrap().enumerate() { let [ikm, salt, info, okm] = row.unwrap(); if let Some(desc) = run_test(ikm, salt, info, okm) { panic!( "\n\ Failed test №{}: {}\n\ ikm:\t{:?}\n\ salt:\t{:?}\n\ info:\t{:?}\n\ okm:\t{:?}\n", i, desc, ikm, salt, info, okm ); } } } }; } new_test!(wycheproof_sha1, "wycheproof-sha1", Hkdf::); new_test!(wycheproof_sha256, "wycheproof-sha256", Hkdf::); new_test!(wycheproof_sha384, "wycheproof-sha384", Hkdf::); new_test!(wycheproof_sha512, "wycheproof-sha512", Hkdf::); new_test!( wycheproof_sha1_simple, "wycheproof-sha1", SimpleHkdf:: ); new_test!( wycheproof_sha256_simple, "wycheproof-sha256", SimpleHkdf:: ); new_test!( wycheproof_sha384_simple, "wycheproof-sha384", SimpleHkdf:: ); new_test!( wycheproof_sha512_simple, "wycheproof-sha512", SimpleHkdf:: ); #[test] fn test_debug_impls() { fn needs_debug() {} needs_debug::>(); needs_debug::>(); }