rsa-0.9.7/.cargo_vcs_info.json0000644000000001360000000000100116360ustar { "git": { "sha1": "551f6e5dcbefa89c030a4fda5534782e6fb8bdb4" }, "path_in_vcs": "" }rsa-0.9.7/.gitattributes000064400000000000000000000001161046102023000133170ustar 00000000000000tests/examples/pkcs1/*.pem text eol=lf tests/examples/pkcs8/*.pem text eol=lf rsa-0.9.7/.github/dependabot.yml000064400000000000000000000003671046102023000146240ustar 00000000000000version: 2 updates: - package-ecosystem: cargo directory: "/" schedule: interval: monthly open-pull-requests-limit: 10 - package-ecosystem: github-actions directory: "/" schedule: interval: monthly open-pull-requests-limit: 10 rsa-0.9.7/.github/workflows/ci.yml000064400000000000000000000041621046102023000151440ustar 00000000000000name: CI on: pull_request: push: branches: - master env: RUSTFLAGS: "-Dwarnings" RUSTDOCFLAGS: "-Dwarnings" jobs: build: runs-on: ubuntu-latest strategy: matrix: rust: - 1.65.0 # MSRV - stable target: - thumbv7em-none-eabi - wasm32-unknown-unknown steps: - uses: actions/checkout@v4 - uses: RustCrypto/actions/cargo-cache@master - uses: dtolnay/rust-toolchain@master with: toolchain: ${{ matrix.rust }} targets: ${{ matrix.target }} - run: cargo build --no-default-features --target ${{ matrix.target }} test: runs-on: ubuntu-latest strategy: matrix: rust: - 1.65.0 # MSRV - stable steps: - uses: actions/checkout@v4 - uses: RustCrypto/actions/cargo-cache@master - uses: dtolnay/rust-toolchain@master with: toolchain: ${{ matrix.rust }} - uses: RustCrypto/actions/cargo-hack-install@master - run: cargo hack test --release --feature-powerset --exclude-features nightly,getrandom,serde - run: cargo test --release --features getrandom - run: cargo test --release --features serde doc: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: RustCrypto/actions/cargo-cache@master - uses: dtolnay/rust-toolchain@master with: toolchain: stable - run: cargo doc --all-features minimal-versions: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: RustCrypto/actions/cargo-cache@master - uses: dtolnay/rust-toolchain@nightly - run: cargo update -Z minimal-versions - uses: dtolnay/rust-toolchain@stable - run: cargo test --release --features getrandom,serde,pkcs5 nightly: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: RustCrypto/actions/cargo-cache@master - uses: dtolnay/rust-toolchain@master with: toolchain: nightly-2023-10-01 - run: cargo test --release --features nightly - run: cargo build --benches rsa-0.9.7/.gitignore000064400000000000000000000000231046102023000124110ustar 00000000000000/target **/*.rs.bk rsa-0.9.7/CHANGELOG.md000064400000000000000000000171211046102023000122410ustar 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.9.7 (2024-11-26) ### Fixed - always validate keys in from_components - do not crash when handling tiny keys in PKCS1v15 ## 0.9.6 (2023-12-01) ### Added - expose a `pss::get_default_pss_signature_algo_id` helper ([#393]) - expose `pkcs1v15::RsaSignatureAssociatedOid` ([#392]) [#392]: https://github.com/RustCrypto/RSA/pull/392 [#393]: https://github.com/RustCrypto/RSA/pull/393 ## 0.9.5 (2023-11-27) ### Added - Adds `RsaPrivateKey::from_primes` and `RsaPrivateKey::from_p_q` methods ([#386]) [#386]: https://github.com/RustCrypto/RSA/pull/386 ## 0.9.4 (2023-11-20) ### Added - Deterministic implementation of prime factors recovery ([#380]) [#380]: https://github.com/RustCrypto/RSA/pull/380 ## 0.9.3 (2023-10-26) ### Added - PKCS#8/SPKI decoding trait impls for `pkcs1v15` keys ([#346]) - `hazmat` feature as a replacement for `expose-internals` ([#352]) ### Changed - Bump `serde` dependency to 1.0.184 ([#360]) ### Removed - Unused dependencies ([#357]) [#346]: https://github.com/RustCrypto/RSA/pull/346 [#352]: https://github.com/RustCrypto/RSA/pull/352 [#357]: https://github.com/RustCrypto/RSA/pull/357 [#360]: https://github.com/RustCrypto/RSA/pull/360 ## 0.9.2 (2023-05-08) ### Fixed - pkcs1v15: have `fmt` impls call `SignatureEncoding::to_bytes` ([#330]) [#330]: https://github.com/RustCrypto/RSA/pull/330 ## 0.9.1 (2023-05-03) ### Fixed - Left pad signatures when encoding ([#325]) [#325]: https://github.com/RustCrypto/RSA/pull/325 ## 0.9.0 (2023-04-27) ### Added - Function to get salt length from RSA PSS keys ([#277]) - `AssociatedAlgorithmIdentifier` implementation ([#278]) - Random key generation for `pss::BlindedSigningKey` ([#295]) - Impl `Signer` for `pss::SigningKey` ([#297]) - Impl `core::hash::Hash` for `RsaPrivateKey` ([#308]) - Impl `ZeroizeOnDrop` for `RsaPrivateKey`, `SigningKey`, `DecryptingKey` ([#311]) - `u64_digit` feature; on-by-default ([#313]) - `AsRef` impl on `RsaPrivateKey` ([#317]) ### Changed - Use namespaced features for `serde` ([#268]) - Bump `pkcs1` to v0.7, `pkcs8` to v0.10; MSRV 1.65 ([#270]) - Rename PKCS#1v1.5 `*_with_prefix` methods ([#290]) - `SigningKey::new` => `SigningKey::new_unprefixed` - `SigningKey::new_with_prefix` => `SigningKey::new` - `VerifyingKey::new` => `VerifyingKey::new_unprefixed` - `VerifyingKey::new_with_prefix` => `VerifyingKey::new` - Rename `Pkcs1v15Sign::new_raw` to `Pkcs1v15Sign::new_unprefixed` ([#293]) - Use digest output size as default PSS salt length ([#294]) - Specify `salt_len` when verifying PSS signatures ([#294]) - Ensure signatures have the expected length and don't overflow the modulus ([#306]) - Improved public key checks ([#307]) - Rename `CRTValue` => `CrtValue` ([#314]) - Traits under `padding` module now located under `traits` module ([#315]) - `PublicKeyParts`/`PrivateKeyParts` now located under `traits` module ([#315]) ### Removed - "Unsalted" PSS support ([#294]) - `EncryptionPrimitive`/`DecriptionPrimitive` traits ([#300]) - `PublicKey`/`PrivateKey` traits ([#300]) - `Zeroize` impl on `RsaPrivateKey`; automatically zeroized on drop ([#311]) - `Deref` impl on `RsaPrivateKey`; use `AsRef` instead ([#317]) - `expose-internals` feature and public access to all functions it gated ([#304]) [#268]: https://github.com/RustCrypto/RSA/pull/268 [#270]: https://github.com/RustCrypto/RSA/pull/270 [#277]: https://github.com/RustCrypto/RSA/pull/277 [#278]: https://github.com/RustCrypto/RSA/pull/278 [#290]: https://github.com/RustCrypto/RSA/pull/290 [#293]: https://github.com/RustCrypto/RSA/pull/293 [#294]: https://github.com/RustCrypto/RSA/pull/294 [#295]: https://github.com/RustCrypto/RSA/pull/295 [#297]: https://github.com/RustCrypto/RSA/pull/297 [#300]: https://github.com/RustCrypto/RSA/pull/300 [#306]: https://github.com/RustCrypto/RSA/pull/306 [#307]: https://github.com/RustCrypto/RSA/pull/307 [#308]: https://github.com/RustCrypto/RSA/pull/308 [#311]: https://github.com/RustCrypto/RSA/pull/311 [#313]: https://github.com/RustCrypto/RSA/pull/313 [#314]: https://github.com/RustCrypto/RSA/pull/314 [#315]: https://github.com/RustCrypto/RSA/pull/315 [#317]: https://github.com/RustCrypto/RSA/pull/317 ## 0.8.2 (2023-03-01) ### Added - Encryption-related traits ([#259]) ### Fixed - Possible panic in `internals::left_pad` ([#262]) - Correct PSS sign/verify when key length is multiple of 8+1 bits ([#263]) [#259]: https://github.com/RustCrypto/RSA/pull/259 [#262]: https://github.com/RustCrypto/RSA/pull/262 [#263]: https://github.com/RustCrypto/RSA/pull/263 ## 0.8.1 (2023-01-20) ### Added - `sha2` feature with `oid` subfeature enabled ([#255]) [#255]: https://github.com/RustCrypto/RSA/pull/255 ## 0.8.0 (2023-01-17) ### Changed - Bump `signature` crate dependency to v2 ([#217], [#249]) - Switch to `CryptoRngCore` marker trait ([#237]) - Make `padding` module private ([#243]) - Refactor `PaddingScheme` into a trait ([#244]) ### Fixed - Benchmark build ([#225]) [#217]: https://github.com/RustCrypto/RSA/pull/217 [#225]: https://github.com/RustCrypto/RSA/pull/225 [#237]: https://github.com/RustCrypto/RSA/pull/237 [#243]: https://github.com/RustCrypto/RSA/pull/243 [#244]: https://github.com/RustCrypto/RSA/pull/244 [#249]: https://github.com/RustCrypto/RSA/pull/249 ## 0.7.2 (2022-11-14) ### Added - Public accessor methods for `PrecomputedValues` ([#221]) - Re-export `signature` crate ([#223]) [#221]: https://github.com/RustCrypto/RSA/pull/221 [#223]: https://github.com/RustCrypto/RSA/pull/223 ## 0.7.1 (2022-10-31) ### Added - Documentation improvements ([#216]) ### Changed - Ensure `PaddingScheme` is `Send` and `Sync` ([#215]) [#215]: https://github.com/RustCrypto/RSA/pull/215 [#216]: https://github.com/RustCrypto/RSA/pull/216 ## 0.7.0 (2022-10-10) [YANKED] NOTE: when computing signatures with this release, make sure to enable the `oid` crate feature of the digest crate you are using when computing the signature (e.g. `sha2`, `sha3`). If the `oid` feature doesn't exist, make sure you're using the latest versions. ### Added - `pkcs1v15` and `pss` modules with `SigningKey`/`VerifyingKey` types ([#174], [#195], [#202], [#207], [#208]) - 4096-bit default max `RsaPublicKey` size ([#176]) - `RsaPublicKey::new_with_max_size` ([#176]) - `RsaPublicKey::new_unchecked` ([#206]) ### Changed - MSRV 1.57 ([#162]) - Bump `pkcs1` to 0.4 ([#162]) - Bump `pkcs8` to 0.9 ([#162]) - `RsaPrivateKey::from_components` is now fallible ([#167]) - pkcs1v15: use `AssociatedOid` for getting the RSA prefix ([#183]) ### Removed - `rng` member from PSS padding scheme ([#173]) - `Hash` removed in favor of using OIDs defined in digest crates ([#183]) [#162]: https://github.com/RustCrypto/RSA/pull/162 [#167]: https://github.com/RustCrypto/RSA/pull/167 [#173]: https://github.com/RustCrypto/RSA/pull/173 [#174]: https://github.com/RustCrypto/RSA/pull/174 [#176]: https://github.com/RustCrypto/RSA/pull/176 [#183]: https://github.com/RustCrypto/RSA/pull/183 [#195]: https://github.com/RustCrypto/RSA/pull/195 [#202]: https://github.com/RustCrypto/RSA/pull/202 [#206]: https://github.com/RustCrypto/RSA/pull/206 [#207]: https://github.com/RustCrypto/RSA/pull/207 [#208]: https://github.com/RustCrypto/RSA/pull/208 ## 0.6.1 (2022-04-11) ## 0.6.0 (2022-04-08) ## 0.5.0 (2021-07-27) ## 0.4.1 (2021-07-26) ## 0.4.0 (2021-03-28) ## 0.3.0 (2020-06-11) ## 0.2.0 (2019-12-11) ## 0.1.4 (2019-10-13) ## 0.1.3 (2019-03-26) ## 0.1.2 (2019-02-25) ## 0.1.1 (2019-02-20) ## 0.1.0 (2018-12-05) rsa-0.9.7/Cargo.toml0000644000000076220000000000100076430ustar # 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.65" name = "rsa" version = "0.9.7" authors = [ "RustCrypto Developers", "dignifiedquire ", ] build = false autobins = false autoexamples = false autotests = false autobenches = false description = "Pure Rust RSA implementation" documentation = "https://docs.rs/rsa" readme = "README.md" keywords = [ "rsa", "encryption", "security", "crypto", ] categories = ["cryptography"] license = "MIT OR Apache-2.0" repository = "https://github.com/RustCrypto/RSA" [package.metadata.docs.rs] features = [ "std", "pem", "serde", "hazmat", "sha2", ] rustdoc-args = [ "--cfg", "docsrs", ] [profile.dev] opt-level = 2 [lib] name = "rsa" path = "src/lib.rs" [[test]] name = "pkcs1" path = "tests/pkcs1.rs" [[test]] name = "pkcs1v15" path = "tests/pkcs1v15.rs" [[test]] name = "pkcs8" path = "tests/pkcs8.rs" [[test]] name = "proptests" path = "tests/proptests.rs" [[bench]] name = "key" path = "benches/key.rs" [dependencies.const-oid] version = "0.9" default-features = false [dependencies.digest] version = "0.10.5" features = [ "alloc", "oid", ] default-features = false [dependencies.num-bigint] version = "0.8.2" features = [ "i128", "prime", "zeroize", ] default-features = false package = "num-bigint-dig" [dependencies.num-integer] version = "0.1.39" default-features = false [dependencies.num-traits] version = "0.2.9" features = ["libm"] default-features = false [dependencies.pkcs1] version = "0.7.5" features = [ "alloc", "pkcs8", ] default-features = false [dependencies.pkcs8] version = "0.10.2" features = ["alloc"] default-features = false [dependencies.rand_core] version = "0.6.4" default-features = false [dependencies.serde] version = "1.0.184" features = ["derive"] optional = true default-features = false [dependencies.sha1] version = "0.10.5" features = ["oid"] optional = true default-features = false [dependencies.sha2] version = "0.10.6" features = ["oid"] optional = true default-features = false [dependencies.signature] version = ">2.0, <2.3" features = [ "alloc", "digest", "rand_core", ] default-features = false [dependencies.spki] version = "0.7.3" features = ["alloc"] default-features = false [dependencies.subtle] version = "2.1.1" default-features = false [dependencies.zeroize] version = "1.5" features = ["alloc"] [dev-dependencies.base64ct] version = "1" features = ["alloc"] [dev-dependencies.hex-literal] version = "0.4.1" [dev-dependencies.proptest] version = "1" [dev-dependencies.rand] version = "0.8" [dev-dependencies.rand_chacha] version = "0.3" [dev-dependencies.rand_core] version = "0.6" default-features = false [dev-dependencies.rand_xorshift] version = "0.3" [dev-dependencies.serde_test] version = "1.0.89" [dev-dependencies.sha1] version = "0.10.5" features = ["oid"] default-features = false [dev-dependencies.sha2] version = "0.10.6" features = ["oid"] default-features = false [dev-dependencies.sha3] version = "0.10.7" features = ["oid"] default-features = false [features] default = [ "std", "pem", "u64_digit", ] getrandom = ["rand_core/getrandom"] hazmat = [] nightly = ["num-bigint/nightly"] pem = [ "pkcs1/pem", "pkcs8/pem", ] pkcs5 = ["pkcs8/encryption"] serde = [ "dep:serde", "num-bigint/serde", ] std = [ "digest/std", "pkcs1/std", "pkcs8/std", "rand_core/std", "signature/std", ] u64_digit = ["num-bigint/u64_digit"] rsa-0.9.7/Cargo.toml.orig000064400000000000000000000052011046102023000133130ustar 00000000000000[package] name = "rsa" version = "0.9.7" authors = ["RustCrypto Developers", "dignifiedquire "] edition = "2021" description = "Pure Rust RSA implementation" license = "MIT OR Apache-2.0" documentation = "https://docs.rs/rsa" repository = "https://github.com/RustCrypto/RSA" keywords = ["rsa", "encryption", "security", "crypto"] categories = ["cryptography"] readme = "README.md" rust-version = "1.65" [dependencies] num-bigint = { version = "0.8.2", features = ["i128", "prime", "zeroize"], default-features = false, package = "num-bigint-dig" } num-traits = { version= "0.2.9", default-features = false, features = ["libm"] } num-integer = { version = "0.1.39", default-features = false } rand_core = { version = "0.6.4", default-features = false } const-oid = { version = "0.9", default-features = false } subtle = { version = "2.1.1", default-features = false } digest = { version = "0.10.5", default-features = false, features = ["alloc", "oid"] } pkcs1 = { version = "0.7.5", default-features = false, features = ["alloc", "pkcs8"] } pkcs8 = { version = "0.10.2", default-features = false, features = ["alloc"] } signature = { version = ">2.0, <2.3", default-features = false , features = ["alloc", "digest", "rand_core"] } spki = { version = "0.7.3", default-features = false, features = ["alloc"] } zeroize = { version = "1.5", features = ["alloc"] } # optional dependencies sha1 = { version = "0.10.5", optional = true, default-features = false, features = ["oid"] } sha2 = { version = "0.10.6", optional = true, default-features = false, features = ["oid"] } serde = { version = "1.0.184", optional = true, default-features = false, features = ["derive"] } [dev-dependencies] base64ct = { version = "1", features = ["alloc"] } hex-literal = "0.4.1" proptest = "1" serde_test = "1.0.89" rand_xorshift = "0.3" rand_chacha = "0.3" rand = "0.8" rand_core = { version = "0.6", default-features = false } sha1 = { version = "0.10.5", default-features = false, features = ["oid"] } sha2 = { version = "0.10.6", default-features = false, features = ["oid"] } sha3 = { version = "0.10.7", default-features = false, features = ["oid"] } [[bench]] name = "key" [features] default = ["std", "pem", "u64_digit"] hazmat = [] getrandom = ["rand_core/getrandom"] nightly = ["num-bigint/nightly"] serde = ["dep:serde", "num-bigint/serde"] pem = ["pkcs1/pem", "pkcs8/pem"] pkcs5 = ["pkcs8/encryption"] u64_digit = ["num-bigint/u64_digit"] std = ["digest/std", "pkcs1/std", "pkcs8/std", "rand_core/std", "signature/std"] [package.metadata.docs.rs] features = ["std", "pem", "serde", "hazmat", "sha2"] rustdoc-args = ["--cfg", "docsrs"] [profile.dev] opt-level = 2 rsa-0.9.7/LICENSE-APACHE000064400000000000000000000251361046102023000123610ustar 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.rsa-0.9.7/LICENSE-MIT000064400000000000000000000017761046102023000120750ustar 00000000000000Permission 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.rsa-0.9.7/README.md000064400000000000000000000107531046102023000117130ustar 00000000000000# [RustCrypto]: RSA [![crates.io][crate-image]][crate-link] [![Documentation][doc-image]][doc-link] [![Build Status][build-image]][build-link] [![dependency status][deps-image]][deps-link] ![MSRV][msrv-image] [![Project Chat][chat-image]][chat-link] A portable RSA implementation in pure Rust. ## Example ```rust use rsa::{Pkcs1v15Encrypt, RsaPrivateKey, RsaPublicKey}; let mut rng = rand::thread_rng(); let bits = 2048; let priv_key = RsaPrivateKey::new(&mut rng, bits).expect("failed to generate a key"); let pub_key = RsaPublicKey::from(&priv_key); // Encrypt let data = b"hello world"; let enc_data = pub_key.encrypt(&mut rng, Pkcs1v15Encrypt, &data[..]).expect("failed to encrypt"); assert_ne!(&data[..], &enc_data[..]); // Decrypt let dec_data = priv_key.decrypt(Pkcs1v15Encrypt, &enc_data).expect("failed to decrypt"); assert_eq!(&data[..], &dec_data[..]); ``` > **Note:** If you encounter unusually slow key generation time while using `RsaPrivateKey::new` you can try to compile in release mode or add the following to your `Cargo.toml`. Key generation is much faster when building with higher optimization levels, but this will increase the compile time a bit. > ```toml > [profile.debug] > opt-level = 3 > ``` > If you don't want to turn on optimizations for all dependencies, > you can only optimize the `num-bigint-dig` dependency. This should > give most of the speedups. > ```toml > [profile.dev.package.num-bigint-dig] > opt-level = 3 > ``` ## Status Currently at Phase 1 (v) 🚧 There will be three phases before `1.0` 🚢 can be released. 1. 🚧 Make it work - [x] Prime generation ✅ - [x] Key generation ✅ - [x] PKCS1v1.5: Encryption & Decryption ✅ - [x] PKCS1v1.5: Sign & Verify ✅ - [ ] PKCS1v1.5 (session key): Encryption & Decryption - [x] OAEP: Encryption & Decryption - [x] PSS: Sign & Verify - [x] Key import & export 2. 🚀 Make it fast - [x] Benchmarks ✅ - [ ] compare to other implementations 🚧 - [ ] optimize 🚧 3. 🔐 Make it secure - [ ] Fuzz testing - [ ] Security Audits ## ⚠️Security Warning This crate has received one [security audit by Include Security][audit], with only one minor finding which has since been addressed. See the [open security issues] on our issue tracker for other known problems. ~~Notably the implementation of [modular exponentiation is not constant time], but timing variability is masked using [random blinding], a commonly used technique.~~ This crate is vulnerable to the [Marvin Attack] which could enable private key recovery by a network attacker (see [RUSTSEC-2023-0071]). You can follow our work on mitigating this issue in [#390]. ## Minimum Supported Rust Version (MSRV) All crates in this repository support Rust 1.65 or higher. In the future MSRV can be changed, but it will be done with a minor version bump. ## License Licensed under either of * [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0) * [MIT license](http://opensource.org/licenses/MIT) at your option. ### Contribution Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions. [//]: # (badges) [crate-image]: https://buildstats.info/crate/rsa [crate-link]: https://crates.io/crates/rsa [doc-image]: https://docs.rs/rsa/badge.svg [doc-link]: https://docs.rs/rsa [build-image]: https://github.com/rustcrypto/RSA/workflows/CI/badge.svg [build-link]: https://github.com/RustCrypto/RSA/actions?query=workflow%3ACI+branch%3Amaster [msrv-image]: https://img.shields.io/badge/rustc-1.65+-blue.svg [chat-image]: https://img.shields.io/badge/zulip-join_chat-blue.svg [chat-link]: https://rustcrypto.zulipchat.com/#narrow/stream/260047-RSA [deps-image]: https://deps.rs/repo/github/RustCrypto/RSA/status.svg [deps-link]: https://deps.rs/repo/github/RustCrypto/RSA [//]: # (links) [RustCrypto]: https://github.com/RustCrypto/ [audit]: https://www.opentech.fund/results/security-safety-audits/deltachat/ [open security issues]: https://github.com/RustCrypto/RSA/issues?q=is%3Aissue+is%3Aopen+label%3Asecurity [modular exponentiation is not constant time]: https://github.com/RustCrypto/RSA/issues/19 [random blinding]: https://en.wikipedia.org/wiki/Blinding_(cryptography) [Marvin Attack]: https://people.redhat.com/~hkario/marvin/ [RUSTSEC-2023-0071]: https://rustsec.org/advisories/RUSTSEC-2023-0071.html [#390]: https://github.com/RustCrypto/RSA/issues/390 rsa-0.9.7/SECURITY.md000064400000000000000000000012641046102023000122220ustar 00000000000000# Security Policy ## Supported Versions Security updates are applied only to the most recent release. ## Reporting a Vulnerability If you have discovered a security vulnerability in this project, please report it privately. **Do not disclose it as a public issue.** This gives us time to work with you to fix the issue before public exposure, reducing the chance that the exploit will be used before a patch is released. Please disclose it at [security advisory](https://github.com/RustCrypto/RSA/security/advisories/new). This project is maintained by a team of volunteers on a reasonable-effort basis. As such, please give us at least 90 days to work on a fix before public exposure. rsa-0.9.7/benches/key.rs000064400000000000000000000067301046102023000132010ustar 00000000000000#![feature(test)] extern crate test; use base64ct::{Base64, Encoding}; use num_bigint::BigUint; use num_traits::{FromPrimitive, Num}; use rand_chacha::{rand_core::SeedableRng, ChaCha8Rng}; use rsa::{Pkcs1v15Encrypt, Pkcs1v15Sign, RsaPrivateKey}; use sha2::{Digest, Sha256}; use test::Bencher; const DECRYPT_VAL: &'static str = "XW4qfrpQDarEMBfPyIYE9UvuOFkbBi0tiGYbIOJPLMNe/LWuPD0BQ7ceqlOlPPcKLinYz0DlnqW3It/V7ae59zw9afA3YIWdq0Ut2BnYL+aJixnqaP+PjsQNcHg6axCF11iNQ4jpXrZDiQcI+q9EEzZDTMsiMxtjfgBQUd8LHT87YoQXDWaFPCVpliACMc8aUk442kH1tc4jEuXwjEjFErvAM/J7VizCdU/dnKrlq2mBDzvZ6hxY9TYHFB/zY6DZPJAgEMUxYWCR9xPJ7X256DV1Kt0Ht33DWoFcgh/pPLM1q9pK0HVxCdclXfZOeCqlrLgZ5Gxv5DM4BtV7Z4m85w=="; fn get_key() -> RsaPrivateKey { RsaPrivateKey::from_components( BigUint::from_str_radix("14314132931241006650998084889274020608918049032671858325988396851334124245188214251956198731333464217832226406088020736932173064754214329009979944037640912127943488972644697423190955557435910767690712778463524983667852819010259499695177313115447116110358524558307947613422897787329221478860907963827160223559690523660574329011927531289655711860504630573766609239332569210831325633840174683944553667352219670930408593321661375473885147973879086994006440025257225431977751512374815915392249179976902953721486040787792801849818254465486633791826766873076617116727073077821584676715609985777563958286637185868165868520557", 10).unwrap(), BigUint::from_u32(3).unwrap(), BigUint::from_str_radix("9542755287494004433998723259516013739278699355114572217325597900889416163458809501304132487555642811888150937392013824621448709836142886006653296025093941418628992648429798282127303704957273845127141852309016655778568546006839666463451542076964744073572349705538631742281931858219480985907271975884773482372966847639853897890615456605598071088189838676728836833012254065983259638538107719766738032720239892094196108713378822882383694456030043492571063441943847195939549773271694647657549658603365629458610273821292232646334717612674519997533901052790334279661754176490593041941863932308687197618671528035670452762731", 10).unwrap(), vec![ BigUint::from_str_radix("130903255182996722426771613606077755295583329135067340152947172868415809027537376306193179624298874215608270802054347609836776473930072411958753044562214537013874103802006369634761074377213995983876788718033850153719421695468704276694983032644416930879093914927146648402139231293035971427838068945045019075433",10).unwrap(), BigUint::from_str_radix("109348945610485453577574767652527472924289229538286649661240938988020367005475727988253438647560958573506159449538793540472829815903949343191091817779240101054552748665267574271163617694640513549693841337820602726596756351006149518830932261246698766355347898158548465400674856021497190430791824869615170301029", 10).unwrap() ], ).unwrap() } #[bench] fn bench_rsa_2048_pkcsv1_decrypt(b: &mut Bencher) { let priv_key = get_key(); let x = Base64::decode_vec(DECRYPT_VAL).unwrap(); b.iter(|| { let res = priv_key.decrypt(Pkcs1v15Encrypt, &x).unwrap(); test::black_box(res); }); } #[bench] fn bench_rsa_2048_pkcsv1_sign_blinded(b: &mut Bencher) { let priv_key = get_key(); let digest = Sha256::digest(b"testing").to_vec(); let mut rng = ChaCha8Rng::from_seed([42; 32]); b.iter(|| { let res = priv_key .sign_with_rng(&mut rng, Pkcs1v15Sign::new::(), &digest) .unwrap(); test::black_box(res); }); } rsa-0.9.7/release.toml000064400000000000000000000001351046102023000127420ustar 00000000000000pre-release-commit-message = "chore({{crate_name}}): release {{version}}" dev-version = falsersa-0.9.7/src/algorithms/generate.rs000064400000000000000000000125441046102023000155340ustar 00000000000000//! Generate prime components for the RSA Private Key use alloc::vec::Vec; use num_bigint::{BigUint, RandPrime}; #[allow(unused_imports)] use num_traits::Float; use num_traits::Zero; use rand_core::CryptoRngCore; use crate::{ algorithms::rsa::{compute_modulus, compute_private_exponent_euler_totient}, errors::{Error, Result}, }; pub struct RsaPrivateKeyComponents { pub n: BigUint, pub e: BigUint, pub d: BigUint, pub primes: Vec, } /// Generates a multi-prime RSA keypair of the given bit size, public exponent, /// and the given random source, as suggested in [1]. Although the public /// keys are compatible (actually, indistinguishable) from the 2-prime case, /// the private keys are not. Thus it may not be possible to export multi-prime /// private keys in certain formats or to subsequently import them into other /// code. /// /// Table 1 in [2] suggests maximum numbers of primes for a given size. /// /// [1]: https://patents.google.com/patent/US4405829A/en /// [2]: http://www.cacr.math.uwaterloo.ca/techreports/2006/cacr2006-16.pdf pub(crate) fn generate_multi_prime_key_with_exp( rng: &mut R, nprimes: usize, bit_size: usize, exp: &BigUint, ) -> Result { if nprimes < 2 { return Err(Error::NprimesTooSmall); } if bit_size < 64 { let prime_limit = (1u64 << (bit_size / nprimes) as u64) as f64; // pi aproximates the number of primes less than prime_limit let mut pi = prime_limit / (prime_limit.ln() - 1f64); // Generated primes start with 0b11, so we can only use a quarter of them. pi /= 4f64; // Use a factor of two to ensure that key generation terminates in a // reasonable amount of time. pi /= 2f64; if pi < nprimes as f64 { return Err(Error::TooFewPrimes); } } let mut primes = vec![BigUint::zero(); nprimes]; let n_final: BigUint; let d_final: BigUint; 'next: loop { let mut todo = bit_size; // `gen_prime` should set the top two bits in each prime. // Thus each prime has the form // p_i = 2^bitlen(p_i) × 0.11... (in base 2). // And the product is: // P = 2^todo × α // where α is the product of nprimes numbers of the form 0.11... // // If α < 1/2 (which can happen for nprimes > 2), we need to // shift todo to compensate for lost bits: the mean value of 0.11... // is 7/8, so todo + shift - nprimes * log2(7/8) ~= bits - 1/2 // will give good results. if nprimes >= 7 { todo += (nprimes - 2) / 5; } for (i, prime) in primes.iter_mut().enumerate() { *prime = rng.gen_prime(todo / (nprimes - i)); todo -= prime.bits(); } // Makes sure that primes is pairwise unequal. for (i, prime1) in primes.iter().enumerate() { for prime2 in primes.iter().take(i) { if prime1 == prime2 { continue 'next; } } } let n = compute_modulus(&primes); if n.bits() != bit_size { // This should never happen for nprimes == 2 because // gen_prime should set the top two bits in each prime. // For nprimes > 2 we hope it does not happen often. continue 'next; } if let Ok(d) = compute_private_exponent_euler_totient(&primes, exp) { n_final = n; d_final = d; break; } } Ok(RsaPrivateKeyComponents { n: n_final, e: exp.clone(), d: d_final, primes, }) } #[cfg(test)] mod tests { use super::*; use num_bigint::BigUint; use num_traits::FromPrimitive; use rand_chacha::{rand_core::SeedableRng, ChaCha8Rng}; const EXP: u64 = 65537; #[test] fn test_impossible_keys() { let mut rng = ChaCha8Rng::from_seed([42; 32]); let exp = BigUint::from_u64(EXP).expect("invalid static exponent"); for i in 0..32 { let _ = generate_multi_prime_key_with_exp(&mut rng, 2, i, &exp); let _ = generate_multi_prime_key_with_exp(&mut rng, 3, i, &exp); let _ = generate_multi_prime_key_with_exp(&mut rng, 4, i, &exp); let _ = generate_multi_prime_key_with_exp(&mut rng, 5, i, &exp); } } macro_rules! key_generation { ($name:ident, $multi:expr, $size:expr) => { #[test] fn $name() { let mut rng = ChaCha8Rng::from_seed([42; 32]); let exp = BigUint::from_u64(EXP).expect("invalid static exponent"); for _ in 0..10 { let components = generate_multi_prime_key_with_exp(&mut rng, $multi, $size, &exp).unwrap(); assert_eq!(components.n.bits(), $size); assert_eq!(components.primes.len(), $multi); } } }; } key_generation!(key_generation_128, 2, 128); key_generation!(key_generation_1024, 2, 1024); key_generation!(key_generation_multi_3_256, 3, 256); key_generation!(key_generation_multi_4_64, 4, 64); key_generation!(key_generation_multi_5_64, 5, 64); key_generation!(key_generation_multi_8_576, 8, 576); key_generation!(key_generation_multi_16_1024, 16, 1024); } rsa-0.9.7/src/algorithms/mgf.rs000064400000000000000000000040441046102023000145070ustar 00000000000000//! Mask generation function common to both PSS and OAEP padding use digest::{Digest, DynDigest, FixedOutputReset}; /// Mask generation function. /// /// Panics if out is larger than 2**32. This is in accordance with RFC 8017 - PKCS #1 B.2.1 pub(crate) fn mgf1_xor(out: &mut [u8], digest: &mut dyn DynDigest, seed: &[u8]) { let mut counter = [0u8; 4]; let mut i = 0; const MAX_LEN: u64 = core::u32::MAX as u64 + 1; assert!(out.len() as u64 <= MAX_LEN); while i < out.len() { let mut digest_input = vec![0u8; seed.len() + 4]; digest_input[0..seed.len()].copy_from_slice(seed); digest_input[seed.len()..].copy_from_slice(&counter); digest.update(digest_input.as_slice()); let digest_output = &*digest.finalize_reset(); let mut j = 0; loop { if j >= digest_output.len() || i >= out.len() { break; } out[i] ^= digest_output[j]; j += 1; i += 1; } inc_counter(&mut counter); } } /// Mask generation function. /// /// Panics if out is larger than 2**32. This is in accordance with RFC 8017 - PKCS #1 B.2.1 pub(crate) fn mgf1_xor_digest(out: &mut [u8], digest: &mut D, seed: &[u8]) where D: Digest + FixedOutputReset, { let mut counter = [0u8; 4]; let mut i = 0; const MAX_LEN: u64 = core::u32::MAX as u64 + 1; assert!(out.len() as u64 <= MAX_LEN); while i < out.len() { Digest::update(digest, seed); Digest::update(digest, counter); let digest_output = digest.finalize_reset(); let mut j = 0; loop { if j >= digest_output.len() || i >= out.len() { break; } out[i] ^= digest_output[j]; j += 1; i += 1; } inc_counter(&mut counter); } } fn inc_counter(counter: &mut [u8; 4]) { for i in (0..4).rev() { counter[i] = counter[i].wrapping_add(1); if counter[i] != 0 { // No overflow return; } } } rsa-0.9.7/src/algorithms/oaep.rs000064400000000000000000000163231046102023000146650ustar 00000000000000//! Encryption and Decryption using [OAEP padding](https://datatracker.ietf.org/doc/html/rfc8017#section-7.1). //! use alloc::string::String; use alloc::vec::Vec; use digest::{Digest, DynDigest, FixedOutputReset}; use rand_core::CryptoRngCore; use subtle::{Choice, ConditionallySelectable, ConstantTimeEq, CtOption}; use zeroize::Zeroizing; use super::mgf::{mgf1_xor, mgf1_xor_digest}; use crate::errors::{Error, Result}; // 2**61 -1 (pow is not const yet) // TODO: This is the maximum for SHA-1, unclear from the RFC what the values are for other hashing functions. const MAX_LABEL_LEN: u64 = 2_305_843_009_213_693_951; #[inline] fn encrypt_internal( rng: &mut R, msg: &[u8], p_hash: &[u8], h_size: usize, k: usize, mut mgf: MGF, ) -> Result>> { if msg.len() + 2 * h_size + 2 > k { return Err(Error::MessageTooLong); } let mut em = Zeroizing::new(vec![0u8; k]); let (_, payload) = em.split_at_mut(1); let (seed, db) = payload.split_at_mut(h_size); rng.fill_bytes(seed); // Data block DB = pHash || PS || 01 || M let db_len = k - h_size - 1; db[0..h_size].copy_from_slice(p_hash); db[db_len - msg.len() - 1] = 1; db[db_len - msg.len()..].copy_from_slice(msg); mgf(seed, db); Ok(em) } /// Encrypts the given message with RSA and the padding scheme from /// [PKCS#1 OAEP]. /// /// The message must be no longer than the length of the public modulus minus /// `2 + (2 * hash.size())`. /// /// [PKCS#1 OAEP]: https://datatracker.ietf.org/doc/html/rfc8017#section-7.1 #[inline] pub(crate) fn oaep_encrypt( rng: &mut R, msg: &[u8], digest: &mut dyn DynDigest, mgf_digest: &mut dyn DynDigest, label: Option, k: usize, ) -> Result>> { let h_size = digest.output_size(); let label = label.unwrap_or_default(); if label.len() as u64 > MAX_LABEL_LEN { return Err(Error::LabelTooLong); } digest.update(label.as_bytes()); let p_hash = digest.finalize_reset(); encrypt_internal(rng, msg, &p_hash, h_size, k, |seed, db| { mgf1_xor(db, mgf_digest, seed); mgf1_xor(seed, mgf_digest, db); }) } /// Encrypts the given message with RSA and the padding scheme from /// [PKCS#1 OAEP]. /// /// The message must be no longer than the length of the public modulus minus /// `2 + (2 * hash.size())`. /// /// [PKCS#1 OAEP]: https://datatracker.ietf.org/doc/html/rfc8017#section-7.1 #[inline] pub(crate) fn oaep_encrypt_digest< R: CryptoRngCore + ?Sized, D: Digest, MGD: Digest + FixedOutputReset, >( rng: &mut R, msg: &[u8], label: Option, k: usize, ) -> Result>> { let h_size = ::output_size(); let label = label.unwrap_or_default(); if label.len() as u64 > MAX_LABEL_LEN { return Err(Error::LabelTooLong); } let p_hash = D::digest(label.as_bytes()); encrypt_internal(rng, msg, &p_hash, h_size, k, |seed, db| { let mut mgf_digest = MGD::new(); mgf1_xor_digest(db, &mut mgf_digest, seed); mgf1_xor_digest(seed, &mut mgf_digest, db); }) } ///Decrypts OAEP padding. /// /// Note that whether this function returns an error or not discloses secret /// information. If an attacker can cause this function to run repeatedly and /// learn whether each instance returned an error then they can decrypt and /// forge signatures as if they had the private key. /// /// See `decrypt_session_key` for a way of solving this problem. /// /// [PKCS#1 OAEP]: https://datatracker.ietf.org/doc/html/rfc8017#section-7.1 #[inline] pub(crate) fn oaep_decrypt( em: &mut [u8], digest: &mut dyn DynDigest, mgf_digest: &mut dyn DynDigest, label: Option, k: usize, ) -> Result> { let h_size = digest.output_size(); let label = label.unwrap_or_default(); if label.len() as u64 > MAX_LABEL_LEN { return Err(Error::Decryption); } digest.update(label.as_bytes()); let expected_p_hash = digest.finalize_reset(); let res = decrypt_inner(em, h_size, &expected_p_hash, k, |seed, db| { mgf1_xor(seed, mgf_digest, db); mgf1_xor(db, mgf_digest, seed); })?; if res.is_none().into() { return Err(Error::Decryption); } let (out, index) = res.unwrap(); Ok(out[index as usize..].to_vec()) } ///Decrypts OAEP padding. /// /// Note that whether this function returns an error or not discloses secret /// information. If an attacker can cause this function to run repeatedly and /// learn whether each instance returned an error then they can decrypt and /// forge signatures as if they had the private key. /// /// See `decrypt_session_key` for a way of solving this problem. /// /// [PKCS#1 OAEP]: https://datatracker.ietf.org/doc/html/rfc8017#section-7.1 #[inline] pub(crate) fn oaep_decrypt_digest( em: &mut [u8], label: Option, k: usize, ) -> Result> { let h_size = ::output_size(); let label = label.unwrap_or_default(); if label.len() as u64 > MAX_LABEL_LEN { return Err(Error::LabelTooLong); } let expected_p_hash = D::digest(label.as_bytes()); let res = decrypt_inner(em, h_size, &expected_p_hash, k, |seed, db| { let mut mgf_digest = MGD::new(); mgf1_xor_digest(seed, &mut mgf_digest, db); mgf1_xor_digest(db, &mut mgf_digest, seed); })?; if res.is_none().into() { return Err(Error::Decryption); } let (out, index) = res.unwrap(); Ok(out[index as usize..].to_vec()) } /// Decrypts OAEP padding. It returns one or zero in valid that indicates whether the /// plaintext was correctly structured. #[inline] fn decrypt_inner( em: &mut [u8], h_size: usize, expected_p_hash: &[u8], k: usize, mut mgf: MGF, ) -> Result, u32)>> { if k < 11 { return Err(Error::Decryption); } if k < h_size * 2 + 2 { return Err(Error::Decryption); } let first_byte_is_zero = em[0].ct_eq(&0u8); let (_, payload) = em.split_at_mut(1); let (seed, db) = payload.split_at_mut(h_size); mgf(seed, db); let hash_are_equal = db[0..h_size].ct_eq(expected_p_hash); // The remainder of the plaintext must be zero or more 0x00, followed // by 0x01, followed by the message. // looking_for_index: 1 if we are still looking for the 0x01 // index: the offset of the first 0x01 byte // zero_before_one: 1 if we saw a non-zero byte before the 1 let mut looking_for_index = Choice::from(1u8); let mut index = 0u32; let mut nonzero_before_one = Choice::from(0u8); for (i, el) in db.iter().skip(h_size).enumerate() { let equals0 = el.ct_eq(&0u8); let equals1 = el.ct_eq(&1u8); index.conditional_assign(&(i as u32), looking_for_index & equals1); looking_for_index &= !equals1; nonzero_before_one |= looking_for_index & !equals0; } let valid = first_byte_is_zero & hash_are_equal & !nonzero_before_one & !looking_for_index; Ok(CtOption::new( (em.to_vec(), index + 2 + (h_size * 2) as u32), valid, )) } rsa-0.9.7/src/algorithms/pad.rs000064400000000000000000000031431046102023000145010ustar 00000000000000//! Special handling for converting the BigUint to u8 vectors use alloc::vec::Vec; use num_bigint::BigUint; use zeroize::Zeroizing; use crate::errors::{Error, Result}; /// Returns a new vector of the given length, with 0s left padded. #[inline] fn left_pad(input: &[u8], padded_len: usize) -> Result> { if input.len() > padded_len { return Err(Error::InvalidPadLen); } let mut out = vec![0u8; padded_len]; out[padded_len - input.len()..].copy_from_slice(input); Ok(out) } /// Converts input to the new vector of the given length, using BE and with 0s left padded. #[inline] pub(crate) fn uint_to_be_pad(input: BigUint, padded_len: usize) -> Result> { left_pad(&input.to_bytes_be(), padded_len) } /// Converts input to the new vector of the given length, using BE and with 0s left padded. #[inline] pub(crate) fn uint_to_zeroizing_be_pad(input: BigUint, padded_len: usize) -> Result> { let m = Zeroizing::new(input); let m = Zeroizing::new(m.to_bytes_be()); left_pad(&m, padded_len) } #[cfg(test)] mod tests { use super::*; #[test] fn test_left_pad() { const INPUT_LEN: usize = 3; let input = vec![0u8; INPUT_LEN]; // input len < padded len let padded = left_pad(&input, INPUT_LEN + 1).unwrap(); assert_eq!(padded.len(), INPUT_LEN + 1); // input len == padded len let padded = left_pad(&input, INPUT_LEN).unwrap(); assert_eq!(padded.len(), INPUT_LEN); // input len > padded len let padded = left_pad(&input, INPUT_LEN - 1); assert!(padded.is_err()); } } rsa-0.9.7/src/algorithms/pkcs1v15.rs000064400000000000000000000146121046102023000153150ustar 00000000000000//! PKCS#1 v1.5 support as described in [RFC8017 § 8.2]. //! //! # Usage //! //! See [code example in the toplevel rustdoc](../index.html#pkcs1-v15-signatures). //! //! [RFC8017 § 8.2]: https://datatracker.ietf.org/doc/html/rfc8017#section-8.2 use alloc::vec::Vec; use digest::Digest; use pkcs8::AssociatedOid; use rand_core::CryptoRngCore; use subtle::{Choice, ConditionallySelectable, ConstantTimeEq}; use zeroize::Zeroizing; use crate::errors::{Error, Result}; /// Fills the provided slice with random values, which are guaranteed /// to not be zero. #[inline] fn non_zero_random_bytes(rng: &mut R, data: &mut [u8]) { rng.fill_bytes(data); for el in data { if *el == 0u8 { // TODO: break after a certain amount of time while *el == 0u8 { rng.fill_bytes(core::slice::from_mut(el)); } } } } /// Applied the padding scheme from PKCS#1 v1.5 for encryption. The message must be no longer than /// the length of the public modulus minus 11 bytes. pub(crate) fn pkcs1v15_encrypt_pad( rng: &mut R, msg: &[u8], k: usize, ) -> Result>> where R: CryptoRngCore + ?Sized, { if msg.len() + 11 > k { return Err(Error::MessageTooLong); } // EM = 0x00 || 0x02 || PS || 0x00 || M let mut em = Zeroizing::new(vec![0u8; k]); em[1] = 2; non_zero_random_bytes(rng, &mut em[2..k - msg.len() - 1]); em[k - msg.len() - 1] = 0; em[k - msg.len()..].copy_from_slice(msg); Ok(em) } /// Removes the encryption padding scheme from PKCS#1 v1.5. /// /// Note that whether this function returns an error or not discloses secret /// information. If an attacker can cause this function to run repeatedly and /// learn whether each instance returned an error then they can decrypt and /// forge signatures as if they had the private key. See /// `decrypt_session_key` for a way of solving this problem. #[inline] pub(crate) fn pkcs1v15_encrypt_unpad(em: Vec, k: usize) -> Result> { let (valid, out, index) = decrypt_inner(em, k)?; if valid == 0 { return Err(Error::Decryption); } Ok(out[index as usize..].to_vec()) } /// Removes the PKCS1v15 padding It returns one or zero in valid that indicates whether the /// plaintext was correctly structured. In either case, the plaintext is /// returned in em so that it may be read independently of whether it was valid /// in order to maintain constant memory access patterns. If the plaintext was /// valid then index contains the index of the original message in em. #[inline] fn decrypt_inner(em: Vec, k: usize) -> Result<(u8, Vec, u32)> { if k < 11 { return Err(Error::Decryption); } let first_byte_is_zero = em[0].ct_eq(&0u8); let second_byte_is_two = em[1].ct_eq(&2u8); // The remainder of the plaintext must be a string of non-zero random // octets, followed by a 0, followed by the message. // looking_for_index: 1 iff we are still looking for the zero. // index: the offset of the first zero byte. let mut looking_for_index = 1u8; let mut index = 0u32; for (i, el) in em.iter().enumerate().skip(2) { let equals0 = el.ct_eq(&0u8); index.conditional_assign(&(i as u32), Choice::from(looking_for_index) & equals0); looking_for_index.conditional_assign(&0u8, equals0); } // The PS padding must be at least 8 bytes long, and it starts two // bytes into em. // TODO: WARNING: THIS MUST BE CONSTANT TIME CHECK: // Ref: https://github.com/dalek-cryptography/subtle/issues/20 // This is currently copy & paste from the constant time impl in // go, but very likely not sufficient. let valid_ps = Choice::from((((2i32 + 8i32 - index as i32 - 1i32) >> 31) & 1) as u8); let valid = first_byte_is_zero & second_byte_is_two & Choice::from(!looking_for_index & 1) & valid_ps; index = u32::conditional_select(&0, &(index + 1), valid); Ok((valid.unwrap_u8(), em, index)) } #[inline] pub(crate) fn pkcs1v15_sign_pad(prefix: &[u8], hashed: &[u8], k: usize) -> Result> { let hash_len = hashed.len(); let t_len = prefix.len() + hashed.len(); if k < t_len + 11 { return Err(Error::MessageTooLong); } // EM = 0x00 || 0x01 || PS || 0x00 || T let mut em = vec![0xff; k]; em[0] = 0; em[1] = 1; em[k - t_len - 1] = 0; em[k - t_len..k - hash_len].copy_from_slice(prefix); em[k - hash_len..k].copy_from_slice(hashed); Ok(em) } #[inline] pub(crate) fn pkcs1v15_sign_unpad(prefix: &[u8], hashed: &[u8], em: &[u8], k: usize) -> Result<()> { let hash_len = hashed.len(); let t_len = prefix.len() + hashed.len(); if k < t_len + 11 { return Err(Error::Verification); } // EM = 0x00 || 0x01 || PS || 0x00 || T let mut ok = em[0].ct_eq(&0u8); ok &= em[1].ct_eq(&1u8); ok &= em[k - hash_len..k].ct_eq(hashed); ok &= em[k - t_len..k - hash_len].ct_eq(prefix); ok &= em[k - t_len - 1].ct_eq(&0u8); for el in em.iter().skip(2).take(k - t_len - 3) { ok &= el.ct_eq(&0xff) } if ok.unwrap_u8() != 1 { return Err(Error::Verification); } Ok(()) } /// prefix = 0x30 0x30 0x06 oid 0x05 0x00 0x04 #[inline] pub(crate) fn pkcs1v15_generate_prefix() -> Vec where D: Digest + AssociatedOid, { let oid = D::OID.as_bytes(); let oid_len = oid.len() as u8; let digest_len = ::output_size() as u8; let mut v = vec![ 0x30, oid_len + 8 + digest_len, 0x30, oid_len + 4, 0x6, oid_len, ]; v.extend_from_slice(oid); v.extend_from_slice(&[0x05, 0x00, 0x04, digest_len]); v } #[cfg(test)] mod tests { use super::*; use rand_chacha::{rand_core::SeedableRng, ChaCha8Rng}; #[test] fn test_non_zero_bytes() { for _ in 0..10 { let mut rng = ChaCha8Rng::from_seed([42; 32]); let mut b = vec![0u8; 512]; non_zero_random_bytes(&mut rng, &mut b); for el in &b { assert_ne!(*el, 0u8); } } } #[test] fn test_encrypt_tiny_no_crash() { let mut rng = ChaCha8Rng::from_seed([42; 32]); let k = 8; let message = vec![1u8; 4]; let res = pkcs1v15_encrypt_pad(&mut rng, &message, k); assert_eq!(res, Err(Error::MessageTooLong)); } } rsa-0.9.7/src/algorithms/pss.rs000064400000000000000000000237241046102023000145510ustar 00000000000000//! Support for the [Probabilistic Signature Scheme] (PSS) a.k.a. RSASSA-PSS. //! //! Designed by Mihir Bellare and Phillip Rogaway. Specified in [RFC8017 § 8.1]. //! //! # Usage //! //! See [code example in the toplevel rustdoc](../index.html#pss-signatures). //! //! [Probabilistic Signature Scheme]: https://en.wikipedia.org/wiki/Probabilistic_signature_scheme //! [RFC8017 § 8.1]: https://datatracker.ietf.org/doc/html/rfc8017#section-8.1 use alloc::vec::Vec; use digest::{Digest, DynDigest, FixedOutputReset}; use subtle::{Choice, ConstantTimeEq}; use super::mgf::{mgf1_xor, mgf1_xor_digest}; use crate::errors::{Error, Result}; pub(crate) fn emsa_pss_encode( m_hash: &[u8], em_bits: usize, salt: &[u8], hash: &mut dyn DynDigest, ) -> Result> { // See [1], section 9.1.1 let h_len = hash.output_size(); let s_len = salt.len(); let em_len = (em_bits + 7) / 8; // 1. If the length of M is greater than the input limitation for the // hash function (2^61 - 1 octets for SHA-1), output "message too // long" and stop. // // 2. Let mHash = Hash(M), an octet string of length hLen. if m_hash.len() != h_len { return Err(Error::InputNotHashed); } // 3. If em_len < h_len + s_len + 2, output "encoding error" and stop. if em_len < h_len + s_len + 2 { // TODO: Key size too small return Err(Error::Internal); } let mut em = vec![0; em_len]; let (db, h) = em.split_at_mut(em_len - h_len - 1); let h = &mut h[..(em_len - 1) - db.len()]; // 4. Generate a random octet string salt of length s_len; if s_len = 0, // then salt is the empty string. // // 5. Let // M' = (0x)00 00 00 00 00 00 00 00 || m_hash || salt; // // M' is an octet string of length 8 + h_len + s_len with eight // initial zero octets. // // 6. Let H = Hash(M'), an octet string of length h_len. let prefix = [0u8; 8]; hash.update(&prefix); hash.update(m_hash); hash.update(salt); let hashed = hash.finalize_reset(); h.copy_from_slice(&hashed); // 7. Generate an octet string PS consisting of em_len - s_len - h_len - 2 // zero octets. The length of PS may be 0. // // 8. Let DB = PS || 0x01 || salt; DB is an octet string of length // emLen - hLen - 1. db[em_len - s_len - h_len - 2] = 0x01; db[em_len - s_len - h_len - 1..].copy_from_slice(salt); // 9. Let dbMask = MGF(H, emLen - hLen - 1). // // 10. Let maskedDB = DB \xor dbMask. mgf1_xor(db, hash, h); // 11. Set the leftmost 8 * em_len - em_bits bits of the leftmost octet in // maskedDB to zero. db[0] &= 0xFF >> (8 * em_len - em_bits); // 12. Let EM = maskedDB || H || 0xbc. em[em_len - 1] = 0xBC; Ok(em) } pub(crate) fn emsa_pss_encode_digest( m_hash: &[u8], em_bits: usize, salt: &[u8], ) -> Result> where D: Digest + FixedOutputReset, { // See [1], section 9.1.1 let h_len = ::output_size(); let s_len = salt.len(); let em_len = (em_bits + 7) / 8; // 1. If the length of M is greater than the input limitation for the // hash function (2^61 - 1 octets for SHA-1), output "message too // long" and stop. // // 2. Let mHash = Hash(M), an octet string of length hLen. if m_hash.len() != h_len { return Err(Error::InputNotHashed); } // 3. If em_len < h_len + s_len + 2, output "encoding error" and stop. if em_len < h_len + s_len + 2 { // TODO: Key size too small return Err(Error::Internal); } let mut em = vec![0; em_len]; let (db, h) = em.split_at_mut(em_len - h_len - 1); let h = &mut h[..(em_len - 1) - db.len()]; // 4. Generate a random octet string salt of length s_len; if s_len = 0, // then salt is the empty string. // // 5. Let // M' = (0x)00 00 00 00 00 00 00 00 || m_hash || salt; // // M' is an octet string of length 8 + h_len + s_len with eight // initial zero octets. // // 6. Let H = Hash(M'), an octet string of length h_len. let prefix = [0u8; 8]; let mut hash = D::new(); Digest::update(&mut hash, prefix); Digest::update(&mut hash, m_hash); Digest::update(&mut hash, salt); let hashed = hash.finalize_reset(); h.copy_from_slice(&hashed); // 7. Generate an octet string PS consisting of em_len - s_len - h_len - 2 // zero octets. The length of PS may be 0. // // 8. Let DB = PS || 0x01 || salt; DB is an octet string of length // emLen - hLen - 1. db[em_len - s_len - h_len - 2] = 0x01; db[em_len - s_len - h_len - 1..].copy_from_slice(salt); // 9. Let dbMask = MGF(H, emLen - hLen - 1). // // 10. Let maskedDB = DB \xor dbMask. mgf1_xor_digest(db, &mut hash, h); // 11. Set the leftmost 8 * em_len - em_bits bits of the leftmost octet in // maskedDB to zero. db[0] &= 0xFF >> (8 * em_len - em_bits); // 12. Let EM = maskedDB || H || 0xbc. em[em_len - 1] = 0xBC; Ok(em) } fn emsa_pss_verify_pre<'a>( m_hash: &[u8], em: &'a mut [u8], em_bits: usize, s_len: usize, h_len: usize, ) -> Result<(&'a mut [u8], &'a mut [u8])> { // 1. If the length of M is greater than the input limitation for the // hash function (2^61 - 1 octets for SHA-1), output "inconsistent" // and stop. // // 2. Let mHash = Hash(M), an octet string of length hLen if m_hash.len() != h_len { return Err(Error::Verification); } // 3. If emLen < hLen + sLen + 2, output "inconsistent" and stop. let em_len = em.len(); //(em_bits + 7) / 8; if em_len < h_len + s_len + 2 { return Err(Error::Verification); } // 4. If the rightmost octet of EM does not have hexadecimal value // 0xbc, output "inconsistent" and stop. if em[em.len() - 1] != 0xBC { return Err(Error::Verification); } // 5. Let maskedDB be the leftmost emLen - hLen - 1 octets of EM, and // let H be the next hLen octets. let (db, h) = em.split_at_mut(em_len - h_len - 1); let h = &mut h[..h_len]; // 6. If the leftmost 8 * em_len - em_bits bits of the leftmost octet in // maskedDB are not all equal to zero, output "inconsistent" and // stop. if db[0] & (0xFF_u8 .checked_shl(8 - (8 * em_len - em_bits) as u32) .unwrap_or(0)) != 0 { return Err(Error::Verification); } Ok((db, h)) } fn emsa_pss_verify_salt(db: &[u8], em_len: usize, s_len: usize, h_len: usize) -> Choice { // 10. If the emLen - hLen - sLen - 2 leftmost octets of DB are not zero // or if the octet at position emLen - hLen - sLen - 1 (the leftmost // position is "position 1") does not have hexadecimal value 0x01, // output "inconsistent" and stop. let (zeroes, rest) = db.split_at(em_len - h_len - s_len - 2); let valid: Choice = zeroes .iter() .fold(Choice::from(1u8), |a, e| a & e.ct_eq(&0x00)); valid & rest[0].ct_eq(&0x01) } pub(crate) fn emsa_pss_verify( m_hash: &[u8], em: &mut [u8], s_len: usize, hash: &mut dyn DynDigest, key_bits: usize, ) -> Result<()> { let em_bits = key_bits - 1; let em_len = (em_bits + 7) / 8; let key_len = (key_bits + 7) / 8; let h_len = hash.output_size(); let em = &mut em[key_len - em_len..]; let (db, h) = emsa_pss_verify_pre(m_hash, em, em_bits, s_len, h_len)?; // 7. Let dbMask = MGF(H, em_len - h_len - 1) // // 8. Let DB = maskedDB \xor dbMask mgf1_xor(db, hash, &*h); // 9. Set the leftmost 8 * emLen - emBits bits of the leftmost octet in DB // to zero. db[0] &= 0xFF >> /*uint*/(8 * em_len - em_bits); let salt_valid = emsa_pss_verify_salt(db, em_len, s_len, h_len); // 11. Let salt be the last s_len octets of DB. let salt = &db[db.len() - s_len..]; // 12. Let // M' = (0x)00 00 00 00 00 00 00 00 || mHash || salt ; // M' is an octet string of length 8 + hLen + sLen with eight // initial zero octets. // // 13. Let H' = Hash(M'), an octet string of length hLen. let prefix = [0u8; 8]; hash.update(&prefix[..]); hash.update(m_hash); hash.update(salt); let h0 = hash.finalize_reset(); // 14. If H = H', output "consistent." Otherwise, output "inconsistent." if (salt_valid & h0.ct_eq(h)).into() { Ok(()) } else { Err(Error::Verification) } } pub(crate) fn emsa_pss_verify_digest( m_hash: &[u8], em: &mut [u8], s_len: usize, key_bits: usize, ) -> Result<()> where D: Digest + FixedOutputReset, { let em_bits = key_bits - 1; let em_len = (em_bits + 7) / 8; let key_len = (key_bits + 7) / 8; let h_len = ::output_size(); let em = &mut em[key_len - em_len..]; let (db, h) = emsa_pss_verify_pre(m_hash, em, em_bits, s_len, h_len)?; let mut hash = D::new(); // 7. Let dbMask = MGF(H, em_len - h_len - 1) // // 8. Let DB = maskedDB \xor dbMask mgf1_xor_digest::(db, &mut hash, &*h); // 9. Set the leftmost 8 * emLen - emBits bits of the leftmost octet in DB // to zero. db[0] &= 0xFF >> /*uint*/(8 * em_len - em_bits); let salt_valid = emsa_pss_verify_salt(db, em_len, s_len, h_len); // 11. Let salt be the last s_len octets of DB. let salt = &db[db.len() - s_len..]; // 12. Let // M' = (0x)00 00 00 00 00 00 00 00 || mHash || salt ; // M' is an octet string of length 8 + hLen + sLen with eight // initial zero octets. // // 13. Let H' = Hash(M'), an octet string of length hLen. let prefix = [0u8; 8]; Digest::update(&mut hash, &prefix[..]); Digest::update(&mut hash, m_hash); Digest::update(&mut hash, salt); let h0 = hash.finalize_reset(); // 14. If H = H', output "consistent." Otherwise, output "inconsistent." if (salt_valid & h0.ct_eq(h)).into() { Ok(()) } else { Err(Error::Verification) } } rsa-0.9.7/src/algorithms/rsa.rs000064400000000000000000000270551046102023000145320ustar 00000000000000//! Generic RSA implementation use alloc::borrow::Cow; use alloc::vec::Vec; use num_bigint::{BigInt, BigUint, IntoBigInt, IntoBigUint, ModInverse, RandBigInt, ToBigInt}; use num_integer::{sqrt, Integer}; use num_traits::{FromPrimitive, One, Pow, Signed, Zero}; use rand_core::CryptoRngCore; use zeroize::{Zeroize, Zeroizing}; use crate::errors::{Error, Result}; use crate::traits::{PrivateKeyParts, PublicKeyParts}; /// ⚠️ Raw RSA encryption of m with the public key. No padding is performed. /// /// # ☢️️ WARNING: HAZARDOUS API ☢️ /// /// Use this function with great care! Raw RSA should never be used without an appropriate padding /// or signature scheme. See the [module-level documentation][crate::hazmat] for more information. #[inline] pub fn rsa_encrypt(key: &K, m: &BigUint) -> Result { Ok(m.modpow(key.e(), key.n())) } /// ⚠️ Performs raw RSA decryption with no padding or error checking. /// /// Returns a plaintext `BigUint`. Performs RSA blinding if an `Rng` is passed. /// /// # ☢️️ WARNING: HAZARDOUS API ☢️ /// /// Use this function with great care! Raw RSA should never be used without an appropriate padding /// or signature scheme. See the [module-level documentation][crate::hazmat] for more information. #[inline] pub fn rsa_decrypt( mut rng: Option<&mut R>, priv_key: &impl PrivateKeyParts, c: &BigUint, ) -> Result { if c >= priv_key.n() { return Err(Error::Decryption); } if priv_key.n().is_zero() { return Err(Error::Decryption); } let mut ir = None; let c = if let Some(ref mut rng) = rng { let (blinded, unblinder) = blind(rng, priv_key, c); ir = Some(unblinder); Cow::Owned(blinded) } else { Cow::Borrowed(c) }; let dp = priv_key.dp(); let dq = priv_key.dq(); let qinv = priv_key.qinv(); let crt_values = priv_key.crt_values(); let m = match (dp, dq, qinv, crt_values) { (Some(dp), Some(dq), Some(qinv), Some(crt_values)) => { // We have the precalculated values needed for the CRT. let p = &priv_key.primes()[0]; let q = &priv_key.primes()[1]; let mut m = c.modpow(dp, p).into_bigint().unwrap(); let mut m2 = c.modpow(dq, q).into_bigint().unwrap(); m -= &m2; let mut primes: Vec<_> = priv_key .primes() .iter() .map(ToBigInt::to_bigint) .map(Option::unwrap) .collect(); while m.is_negative() { m += &primes[0]; } m *= qinv; m %= &primes[0]; m *= &primes[1]; m += &m2; let mut c = c.into_owned().into_bigint().unwrap(); for (i, value) in crt_values.iter().enumerate() { let prime = &primes[2 + i]; m2 = c.modpow(&value.exp, prime); m2 -= &m; m2 *= &value.coeff; m2 %= prime; while m2.is_negative() { m2 += prime; } m2 *= &value.r; m += &m2; } // clear tmp values for prime in primes.iter_mut() { prime.zeroize(); } primes.clear(); c.zeroize(); m2.zeroize(); m.into_biguint().expect("failed to decrypt") } _ => c.modpow(priv_key.d(), priv_key.n()), }; match ir { Some(ref ir) => { // unblind Ok(unblind(priv_key, &m, ir)) } None => Ok(m), } } /// ⚠️ Performs raw RSA decryption with no padding. /// /// Returns a plaintext `BigUint`. Performs RSA blinding if an `Rng` is passed. This will also /// check for errors in the CRT computation. /// /// # ☢️️ WARNING: HAZARDOUS API ☢️ /// /// Use this function with great care! Raw RSA should never be used without an appropriate padding /// or signature scheme. See the [module-level documentation][crate::hazmat] for more information. #[inline] pub fn rsa_decrypt_and_check( priv_key: &impl PrivateKeyParts, rng: Option<&mut R>, c: &BigUint, ) -> Result { let m = rsa_decrypt(rng, priv_key, c)?; // In order to defend against errors in the CRT computation, m^e is // calculated, which should match the original ciphertext. let check = rsa_encrypt(priv_key, &m)?; if c != &check { return Err(Error::Internal); } Ok(m) } /// Returns the blinded c, along with the unblinding factor. fn blind( rng: &mut R, key: &K, c: &BigUint, ) -> (BigUint, BigUint) { // Blinding involves multiplying c by r^e. // Then the decryption operation performs (m^e * r^e)^d mod n // which equals mr mod n. The factor of r can then be removed // by multiplying by the multiplicative inverse of r. let mut r: BigUint; let mut ir: Option; let unblinder; loop { r = rng.gen_biguint_below(key.n()); if r.is_zero() { r = BigUint::one(); } ir = r.clone().mod_inverse(key.n()); if let Some(ir) = ir { if let Some(ub) = ir.into_biguint() { unblinder = ub; break; } } } let c = { let mut rpowe = r.modpow(key.e(), key.n()); // N != 0 let mut c = c * &rpowe; c %= key.n(); rpowe.zeroize(); c }; (c, unblinder) } /// Given an m and and unblinding factor, unblind the m. fn unblind(key: &impl PublicKeyParts, m: &BigUint, unblinder: &BigUint) -> BigUint { (m * unblinder) % key.n() } /// The following (deterministic) algorithm also recovers the prime factors `p` and `q` of a modulus `n`, given the /// public exponent `e` and private exponent `d` using the method described in /// [NIST 800-56B Appendix C.2](https://nvlpubs.nist.gov/nistpubs/SpecialPublications/NIST.SP.800-56Br2.pdf). pub fn recover_primes(n: &BigUint, e: &BigUint, d: &BigUint) -> Result<(BigUint, BigUint)> { // Check precondition let two = BigUint::from_u8(2).unwrap(); if e <= &two.pow(16u32) || e >= &two.pow(256u32) { return Err(Error::InvalidArguments); } // 1. Let a = (de – 1) × GCD(n – 1, de – 1). let one = BigUint::one(); let a = Zeroizing::new((d * e - &one) * (n - &one).gcd(&(d * e - &one))); // 2. Let m = floor(a /n) and r = a – m n, so that a = m n + r and 0 ≤ r < n. let m = Zeroizing::new(&*a / n); let r = Zeroizing::new(&*a - &*m * n); // 3. Let b = ( (n – r)/(m + 1) ) + 1; if b is not an integer or b^2 ≤ 4n, then output an error indicator, // and exit without further processing. let modulus_check = Zeroizing::new((n - &*r) % (&*m + &one)); if !modulus_check.is_zero() { return Err(Error::InvalidArguments); } let b = Zeroizing::new((n - &*r) / (&*m + &one) + one); let four = BigUint::from_u8(4).unwrap(); let four_n = Zeroizing::new(n * four); let b_squared = Zeroizing::new(b.pow(2u32)); if *b_squared <= *four_n { return Err(Error::InvalidArguments); } let b_squared_minus_four_n = Zeroizing::new(&*b_squared - &*four_n); // 4. Let ϒ be the positive square root of b^2 – 4n; if ϒ is not an integer, // then output an error indicator, and exit without further processing. let y = Zeroizing::new(sqrt((*b_squared_minus_four_n).clone())); let y_squared = Zeroizing::new(y.pow(2u32)); let sqrt_is_whole_number = y_squared == b_squared_minus_four_n; if !sqrt_is_whole_number { return Err(Error::InvalidArguments); } let p = (&*b + &*y) / &two; let q = (&*b - &*y) / two; Ok((p, q)) } /// Compute the modulus of a key from its primes. pub(crate) fn compute_modulus(primes: &[BigUint]) -> BigUint { primes.iter().product() } /// Compute the private exponent from its primes (p and q) and public exponent /// This uses Euler's totient function #[inline] pub(crate) fn compute_private_exponent_euler_totient( primes: &[BigUint], exp: &BigUint, ) -> Result { if primes.len() < 2 { return Err(Error::InvalidPrime); } let mut totient = BigUint::one(); for prime in primes { totient *= prime - BigUint::one(); } // NOTE: `mod_inverse` checks if `exp` evenly divides `totient` and returns `None` if so. // This ensures that `exp` is not a factor of any `(prime - 1)`. if let Some(d) = exp.mod_inverse(totient) { Ok(d.to_biguint().unwrap()) } else { // `exp` evenly divides `totient` Err(Error::InvalidPrime) } } /// Compute the private exponent from its primes (p and q) and public exponent /// /// This is using the method defined by /// [NIST 800-56B Section 6.2.1](https://nvlpubs.nist.gov/nistpubs/SpecialPublications/NIST.SP.800-56Br2.pdf#page=47). /// (Carmichael function) /// /// FIPS 186-4 **requires** the private exponent to be less than λ(n), which would /// make Euler's totiem unreliable. #[inline] pub(crate) fn compute_private_exponent_carmicheal( p: &BigUint, q: &BigUint, exp: &BigUint, ) -> Result { let p1 = p - BigUint::one(); let q1 = q - BigUint::one(); let lcm = p1.lcm(&q1); if let Some(d) = exp.mod_inverse(lcm) { Ok(d.to_biguint().unwrap()) } else { // `exp` evenly divides `lcm` Err(Error::InvalidPrime) } } #[cfg(test)] mod tests { use num_traits::FromPrimitive; use super::*; #[test] fn recover_primes_works() { let n = BigUint::parse_bytes(b"00d397b84d98a4c26138ed1b695a8106ead91d553bf06041b62d3fdc50a041e222b8f4529689c1b82c5e71554f5dd69fa2f4b6158cf0dbeb57811a0fc327e1f28e74fe74d3bc166c1eabdc1b8b57b934ca8be5b00b4f29975bcc99acaf415b59bb28a6782bb41a2c3c2976b3c18dbadef62f00c6bb226640095096c0cc60d22fe7ef987d75c6a81b10d96bf292028af110dc7cc1bbc43d22adab379a0cd5d8078cc780ff5cd6209dea34c922cf784f7717e428d75b5aec8ff30e5f0141510766e2e0ab8d473c84e8710b2b98227c3db095337ad3452f19e2b9bfbccdd8148abf6776fa552775e6e75956e45229ae5a9c46949bab1e622f0e48f56524a84ed3483b", 16).unwrap(); let e = BigUint::from_u64(65537).unwrap(); let d = BigUint::parse_bytes(b"00c4e70c689162c94c660828191b52b4d8392115df486a9adbe831e458d73958320dc1b755456e93701e9702d76fb0b92f90e01d1fe248153281fe79aa9763a92fae69d8d7ecd144de29fa135bd14f9573e349e45031e3b76982f583003826c552e89a397c1a06bd2163488630d92e8c2bb643d7abef700da95d685c941489a46f54b5316f62b5d2c3a7f1bbd134cb37353a44683fdc9d95d36458de22f6c44057fe74a0a436c4308f73f4da42f35c47ac16a7138d483afc91e41dc3a1127382e0c0f5119b0221b4fc639d6b9c38177a6de9b526ebd88c38d7982c07f98a0efd877d508aae275b946915c02e2e1106d175d74ec6777f5e80d12c053d9c7be1e341", 16).unwrap(); let p = BigUint::parse_bytes(b"00f827bbf3a41877c7cc59aebf42ed4b29c32defcb8ed96863d5b090a05a8930dd624a21c9dcf9838568fdfa0df65b8462a5f2ac913d6c56f975532bd8e78fb07bd405ca99a484bcf59f019bbddcb3933f2bce706300b4f7b110120c5df9018159067c35da3061a56c8635a52b54273b31271b4311f0795df6021e6355e1a42e61",16).unwrap(); let q = BigUint::parse_bytes(b"00da4817ce0089dd36f2ade6a3ff410c73ec34bf1b4f6bda38431bfede11cef1f7f6efa70e5f8063a3b1f6e17296ffb15feefa0912a0325b8d1fd65a559e717b5b961ec345072e0ec5203d03441d29af4d64054a04507410cf1da78e7b6119d909ec66e6ad625bf995b279a4b3c5be7d895cd7c5b9c4c497fde730916fcdb4e41b", 16).unwrap(); let (mut p1, mut q1) = recover_primes(&n, &e, &d).unwrap(); if p1 < q1 { std::mem::swap(&mut p1, &mut q1); } assert_eq!(p, p1); assert_eq!(q, q1); } } rsa-0.9.7/src/algorithms.rs000064400000000000000000000002641046102023000137360ustar 00000000000000//! Useful algorithms related to RSA. mod mgf; pub(crate) mod generate; pub(crate) mod oaep; pub(crate) mod pad; pub(crate) mod pkcs1v15; pub(crate) mod pss; pub(crate) mod rsa; rsa-0.9.7/src/dummy_rng.rs000064400000000000000000000011171046102023000135640ustar 00000000000000use rand_core::{CryptoRng, RngCore}; /// This is a dummy RNG for cases when we need a concrete RNG type /// which does not get used. #[derive(Copy, Clone)] pub(crate) struct DummyRng; impl RngCore for DummyRng { fn next_u32(&mut self) -> u32 { unimplemented!(); } fn next_u64(&mut self) -> u64 { unimplemented!(); } fn fill_bytes(&mut self, _: &mut [u8]) { unimplemented!(); } fn try_fill_bytes(&mut self, _: &mut [u8]) -> core::result::Result<(), rand_core::Error> { unimplemented!(); } } impl CryptoRng for DummyRng {} rsa-0.9.7/src/encoding.rs000064400000000000000000000112571046102023000133570ustar 00000000000000//! PKCS#1 and PKCS#8 encoding support. //! //! Note: PKCS#1 support is achieved through a blanket impl of the //! `pkcs1` crate's traits for types which impl the `pkcs8` crate's traits. use crate::{ traits::{PrivateKeyParts, PublicKeyParts}, BigUint, RsaPrivateKey, RsaPublicKey, }; use core::convert::{TryFrom, TryInto}; use pkcs8::{der::Encode, Document, EncodePrivateKey, EncodePublicKey, SecretDocument}; use zeroize::Zeroizing; /// Verify that the `AlgorithmIdentifier` for a key is correct. fn verify_algorithm_id(algorithm: &pkcs8::AlgorithmIdentifierRef) -> pkcs8::spki::Result<()> { algorithm.assert_algorithm_oid(pkcs1::ALGORITHM_OID)?; if algorithm.parameters_any()? != pkcs8::der::asn1::Null.into() { return Err(pkcs8::spki::Error::KeyMalformed); } Ok(()) } impl TryFrom> for RsaPrivateKey { type Error = pkcs8::Error; fn try_from(private_key_info: pkcs8::PrivateKeyInfo<'_>) -> pkcs8::Result { verify_algorithm_id(&private_key_info.algorithm)?; let pkcs1_key = pkcs1::RsaPrivateKey::try_from(private_key_info.private_key)?; // Multi-prime RSA keys not currently supported if pkcs1_key.version() != pkcs1::Version::TwoPrime { return Err(pkcs1::Error::Version.into()); } let n = BigUint::from_bytes_be(pkcs1_key.modulus.as_bytes()); let e = BigUint::from_bytes_be(pkcs1_key.public_exponent.as_bytes()); let d = BigUint::from_bytes_be(pkcs1_key.private_exponent.as_bytes()); let prime1 = BigUint::from_bytes_be(pkcs1_key.prime1.as_bytes()); let prime2 = BigUint::from_bytes_be(pkcs1_key.prime2.as_bytes()); let primes = vec![prime1, prime2]; RsaPrivateKey::from_components(n, e, d, primes).map_err(|_| pkcs8::Error::KeyMalformed) } } impl TryFrom> for RsaPublicKey { type Error = pkcs8::spki::Error; fn try_from(spki: pkcs8::SubjectPublicKeyInfoRef<'_>) -> pkcs8::spki::Result { verify_algorithm_id(&spki.algorithm)?; let pkcs1_key = pkcs1::RsaPublicKey::try_from( spki.subject_public_key .as_bytes() .ok_or(pkcs8::spki::Error::KeyMalformed)?, )?; let n = BigUint::from_bytes_be(pkcs1_key.modulus.as_bytes()); let e = BigUint::from_bytes_be(pkcs1_key.public_exponent.as_bytes()); RsaPublicKey::new(n, e).map_err(|_| pkcs8::spki::Error::KeyMalformed) } } impl EncodePrivateKey for RsaPrivateKey { fn to_pkcs8_der(&self) -> pkcs8::Result { // Check if the key is multi prime if self.primes.len() > 2 { return Err(pkcs1::Error::Version.into()); } let modulus = self.n().to_bytes_be(); let public_exponent = self.e().to_bytes_be(); let private_exponent = Zeroizing::new(self.d().to_bytes_be()); let prime1 = Zeroizing::new(self.primes[0].to_bytes_be()); let prime2 = Zeroizing::new(self.primes[1].to_bytes_be()); let exponent1 = Zeroizing::new((self.d() % (&self.primes[0] - 1u8)).to_bytes_be()); let exponent2 = Zeroizing::new((self.d() % (&self.primes[1] - 1u8)).to_bytes_be()); let coefficient = Zeroizing::new( self.crt_coefficient() .ok_or(pkcs1::Error::Crypto)? .to_bytes_be(), ); let private_key = pkcs1::RsaPrivateKey { modulus: pkcs1::UintRef::new(&modulus)?, public_exponent: pkcs1::UintRef::new(&public_exponent)?, private_exponent: pkcs1::UintRef::new(&private_exponent)?, prime1: pkcs1::UintRef::new(&prime1)?, prime2: pkcs1::UintRef::new(&prime2)?, exponent1: pkcs1::UintRef::new(&exponent1)?, exponent2: pkcs1::UintRef::new(&exponent2)?, coefficient: pkcs1::UintRef::new(&coefficient)?, other_prime_infos: None, } .to_der()?; pkcs8::PrivateKeyInfo::new(pkcs1::ALGORITHM_ID, private_key.as_ref()).try_into() } } impl EncodePublicKey for RsaPublicKey { fn to_public_key_der(&self) -> pkcs8::spki::Result { let modulus = self.n().to_bytes_be(); let public_exponent = self.e().to_bytes_be(); let subject_public_key = pkcs1::RsaPublicKey { modulus: pkcs1::UintRef::new(&modulus)?, public_exponent: pkcs1::UintRef::new(&public_exponent)?, } .to_der()?; pkcs8::SubjectPublicKeyInfoRef { algorithm: pkcs1::ALGORITHM_ID, subject_public_key: pkcs8::der::asn1::BitStringRef::new( 0, subject_public_key.as_ref(), )?, } .try_into() } } rsa-0.9.7/src/errors.rs000064400000000000000000000065701046102023000131070ustar 00000000000000//! Error types. /// Alias for [`core::result::Result`] with the `rsa` crate's [`Error`] type. pub type Result = core::result::Result; /// Error types #[derive(Debug, Eq, PartialEq)] #[non_exhaustive] pub enum Error { /// Invalid padding scheme. InvalidPaddingScheme, /// Decryption error. Decryption, /// Verification error. Verification, /// Message too long. MessageTooLong, /// Input must be hashed. InputNotHashed, /// Number of primes must be 2 or greater. NprimesTooSmall, /// Too few primes of a given length to generate an RSA key. TooFewPrimes, /// Invalid prime value. InvalidPrime, /// Invalid modulus. InvalidModulus, /// Invalid exponent. InvalidExponent, /// Invalid coefficient. InvalidCoefficient, /// Modulus too large. ModulusTooLarge, /// Public exponent too small. PublicExponentTooSmall, /// Public exponent too large. PublicExponentTooLarge, /// PKCS#1 error. Pkcs1(pkcs1::Error), /// PKCS#8 error. Pkcs8(pkcs8::Error), /// Internal error. Internal, /// Label too long. LabelTooLong, /// Invalid padding length. InvalidPadLen, /// Invalid arguments. InvalidArguments, } #[cfg(feature = "std")] impl std::error::Error for Error {} impl core::fmt::Display for Error { fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { match self { Error::InvalidPaddingScheme => write!(f, "invalid padding scheme"), Error::Decryption => write!(f, "decryption error"), Error::Verification => write!(f, "verification error"), Error::MessageTooLong => write!(f, "message too long"), Error::InputNotHashed => write!(f, "input must be hashed"), Error::NprimesTooSmall => write!(f, "nprimes must be >= 2"), Error::TooFewPrimes => { write!(f, "too few primes of given length to generate an RSA key") } Error::InvalidPrime => write!(f, "invalid prime value"), Error::InvalidModulus => write!(f, "invalid modulus"), Error::InvalidExponent => write!(f, "invalid exponent"), Error::InvalidCoefficient => write!(f, "invalid coefficient"), Error::ModulusTooLarge => write!(f, "modulus too large"), Error::PublicExponentTooSmall => write!(f, "public exponent too small"), Error::PublicExponentTooLarge => write!(f, "public exponent too large"), Error::Pkcs1(err) => write!(f, "{}", err), Error::Pkcs8(err) => write!(f, "{}", err), Error::Internal => write!(f, "internal error"), Error::LabelTooLong => write!(f, "label too long"), Error::InvalidPadLen => write!(f, "invalid padding length"), Error::InvalidArguments => write!(f, "invalid arguments"), } } } impl From for Error { fn from(err: pkcs1::Error) -> Error { Error::Pkcs1(err) } } impl From for Error { fn from(err: pkcs8::Error) -> Error { Error::Pkcs8(err) } } #[cfg(feature = "std")] impl From for signature::Error { fn from(err: Error) -> Self { Self::from_source(err) } } #[cfg(not(feature = "std"))] impl From for signature::Error { fn from(_err: Error) -> Self { Self::new() } } rsa-0.9.7/src/hazmat.rs000064400000000000000000000012551046102023000130520ustar 00000000000000//! ⚠️ Low-level "hazmat" RSA functions. //! //! # ☢️️ WARNING: HAZARDOUS API ☢️ //! //! This module holds functions that apply RSA's core encryption and decryption //! primitives to raw data without adding or removing appropriate padding. A //! well-reviewed padding scheme is crucial to the security of RSA, so there are //! very few valid uses cases for this API. It's intended to be used for //! implementing well-reviewed higher-level constructions. //! //! We do NOT recommend using it to implement any algorithm which has not //! received extensive peer review by cryptographers. pub use crate::algorithms::rsa::{rsa_decrypt, rsa_decrypt_and_check, rsa_encrypt}; rsa-0.9.7/src/key.rs000064400000000000000000001000101046102023000123430ustar 00000000000000use alloc::vec::Vec; use core::hash::{Hash, Hasher}; use num_bigint::traits::ModInverse; use num_bigint::Sign::Plus; use num_bigint::{BigInt, BigUint}; use num_integer::Integer; use num_traits::{FromPrimitive, One, ToPrimitive}; use rand_core::CryptoRngCore; #[cfg(feature = "serde")] use serde::{Deserialize, Serialize}; use zeroize::{Zeroize, ZeroizeOnDrop}; use crate::algorithms::generate::generate_multi_prime_key_with_exp; use crate::algorithms::rsa::{ compute_modulus, compute_private_exponent_carmicheal, compute_private_exponent_euler_totient, recover_primes, }; use crate::dummy_rng::DummyRng; use crate::errors::{Error, Result}; use crate::traits::{PaddingScheme, PrivateKeyParts, PublicKeyParts, SignatureScheme}; use crate::CrtValue; /// Represents the public part of an RSA key. #[derive(Debug, Clone, Hash, PartialEq, Eq)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] pub struct RsaPublicKey { /// Modulus: product of prime numbers `p` and `q` n: BigUint, /// Public exponent: power to which a plaintext message is raised in /// order to encrypt it. /// /// Typically 0x10001 (65537) e: BigUint, } /// Represents a whole RSA key, public and private parts. #[derive(Debug, Clone)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] pub struct RsaPrivateKey { /// Public components of the private key. pubkey_components: RsaPublicKey, /// Private exponent pub(crate) d: BigUint, /// Prime factors of N, contains >= 2 elements. pub(crate) primes: Vec, /// precomputed values to speed up private operations #[cfg_attr(feature = "serde", serde(skip))] pub(crate) precomputed: Option, } impl Eq for RsaPrivateKey {} impl PartialEq for RsaPrivateKey { #[inline] fn eq(&self, other: &RsaPrivateKey) -> bool { self.pubkey_components == other.pubkey_components && self.d == other.d && self.primes == other.primes } } impl AsRef for RsaPrivateKey { fn as_ref(&self) -> &RsaPublicKey { &self.pubkey_components } } impl Hash for RsaPrivateKey { fn hash(&self, state: &mut H) { // Domain separator for RSA private keys state.write(b"RsaPrivateKey"); Hash::hash(&self.pubkey_components, state); } } impl Drop for RsaPrivateKey { fn drop(&mut self) { self.d.zeroize(); self.primes.zeroize(); self.precomputed.zeroize(); } } impl ZeroizeOnDrop for RsaPrivateKey {} #[derive(Debug, Clone)] pub(crate) struct PrecomputedValues { /// D mod (P-1) pub(crate) dp: BigUint, /// D mod (Q-1) pub(crate) dq: BigUint, /// Q^-1 mod P pub(crate) qinv: BigInt, /// CRTValues is used for the 3rd and subsequent primes. Due to a /// historical accident, the CRT for the first two primes is handled /// differently in PKCS#1 and interoperability is sufficiently /// important that we mirror this. pub(crate) crt_values: Vec, } impl Zeroize for PrecomputedValues { fn zeroize(&mut self) { self.dp.zeroize(); self.dq.zeroize(); self.qinv.zeroize(); for val in self.crt_values.iter_mut() { val.zeroize(); } self.crt_values.clear(); } } impl Drop for PrecomputedValues { fn drop(&mut self) { self.zeroize(); } } impl From for RsaPublicKey { fn from(private_key: RsaPrivateKey) -> Self { (&private_key).into() } } impl From<&RsaPrivateKey> for RsaPublicKey { fn from(private_key: &RsaPrivateKey) -> Self { let n = private_key.n().clone(); let e = private_key.e().clone(); RsaPublicKey { n, e } } } impl PublicKeyParts for RsaPublicKey { fn n(&self) -> &BigUint { &self.n } fn e(&self) -> &BigUint { &self.e } } impl RsaPublicKey { /// Encrypt the given message. pub fn encrypt( &self, rng: &mut R, padding: P, msg: &[u8], ) -> Result> { padding.encrypt(rng, self, msg) } /// Verify a signed message. /// /// `hashed` must be the result of hashing the input using the hashing function /// passed in through `hash`. /// /// If the message is valid `Ok(())` is returned, otherwise an `Err` indicating failure. pub fn verify(&self, scheme: S, hashed: &[u8], sig: &[u8]) -> Result<()> { scheme.verify(self, hashed, sig) } } impl RsaPublicKey { /// Minimum value of the public exponent `e`. pub const MIN_PUB_EXPONENT: u64 = 2; /// Maximum value of the public exponent `e`. pub const MAX_PUB_EXPONENT: u64 = (1 << 33) - 1; /// Maximum size of the modulus `n` in bits. pub const MAX_SIZE: usize = 4096; /// Create a new public key from its components. /// /// This function accepts public keys with a modulus size up to 4096-bits, /// i.e. [`RsaPublicKey::MAX_SIZE`]. pub fn new(n: BigUint, e: BigUint) -> Result { Self::new_with_max_size(n, e, Self::MAX_SIZE) } /// Create a new public key from its components. pub fn new_with_max_size(n: BigUint, e: BigUint, max_size: usize) -> Result { let k = Self { n, e }; check_public_with_max_size(&k, max_size)?; Ok(k) } /// Create a new public key, bypassing checks around the modulus and public /// exponent size. /// /// This method is not recommended, and only intended for unusual use cases. /// Most applications should use [`RsaPublicKey::new`] or /// [`RsaPublicKey::new_with_max_size`] instead. pub fn new_unchecked(n: BigUint, e: BigUint) -> Self { Self { n, e } } } impl PublicKeyParts for RsaPrivateKey { fn n(&self) -> &BigUint { &self.pubkey_components.n } fn e(&self) -> &BigUint { &self.pubkey_components.e } } impl RsaPrivateKey { /// Default exponent for RSA keys. const EXP: u64 = 65537; /// Generate a new Rsa key pair of the given bit size using the passed in `rng`. pub fn new(rng: &mut R, bit_size: usize) -> Result { let exp = BigUint::from_u64(Self::EXP).expect("invalid static exponent"); Self::new_with_exp(rng, bit_size, &exp) } /// Generate a new RSA key pair of the given bit size and the public exponent /// using the passed in `rng`. /// /// Unless you have specific needs, you should use `RsaPrivateKey::new` instead. pub fn new_with_exp( rng: &mut R, bit_size: usize, exp: &BigUint, ) -> Result { let components = generate_multi_prime_key_with_exp(rng, 2, bit_size, exp)?; RsaPrivateKey::from_components(components.n, components.e, components.d, components.primes) } /// Constructs an RSA key pair from individual components: /// /// - `n`: RSA modulus /// - `e`: public exponent (i.e. encrypting exponent) /// - `d`: private exponent (i.e. decrypting exponent) /// - `primes`: prime factors of `n`: typically two primes `p` and `q`. More than two primes can /// be provided for multiprime RSA, however this is generally not recommended. If no `primes` /// are provided, a prime factor recovery algorithm will be employed to attempt to recover the /// factors (as described in [NIST SP 800-56B Revision 2] Appendix C.2). This algorithm only /// works if there are just two prime factors `p` and `q` (as opposed to multiprime), and `e` /// is between 2^16 and 2^256. /// /// [NIST SP 800-56B Revision 2]: https://nvlpubs.nist.gov/nistpubs/SpecialPublications/NIST.SP.800-56Br2.pdf pub fn from_components( n: BigUint, e: BigUint, d: BigUint, mut primes: Vec, ) -> Result { if primes.len() < 2 { if !primes.is_empty() { return Err(Error::NprimesTooSmall); } // Recover `p` and `q` from `d`. // See method in Appendix C.2: https://nvlpubs.nist.gov/nistpubs/SpecialPublications/NIST.SP.800-56Br2.pdf let (p, q) = recover_primes(&n, &e, &d)?; primes.push(p); primes.push(q); } let mut k = RsaPrivateKey { pubkey_components: RsaPublicKey { n, e }, d, primes, precomputed: None, }; // Alaways validate the key, to ensure precompute can't fail k.validate()?; // precompute when possible, ignore error otherwise. let _ = k.precompute(); Ok(k) } /// Constructs an RSA key pair from its two primes p and q. /// /// This will rebuild the private exponent and the modulus. /// /// Private exponent will be rebuilt using the method defined in /// [NIST 800-56B Section 6.2.1](https://nvlpubs.nist.gov/nistpubs/SpecialPublications/NIST.SP.800-56Br2.pdf#page=47). pub fn from_p_q(p: BigUint, q: BigUint, public_exponent: BigUint) -> Result { if p == q { return Err(Error::InvalidPrime); } let n = compute_modulus(&[p.clone(), q.clone()]); let d = compute_private_exponent_carmicheal(&p, &q, &public_exponent)?; Self::from_components(n, public_exponent, d, vec![p, q]) } /// Constructs an RSA key pair from its primes. /// /// This will rebuild the private exponent and the modulus. pub fn from_primes(primes: Vec, public_exponent: BigUint) -> Result { if primes.len() < 2 { return Err(Error::NprimesTooSmall); } // Makes sure that primes is pairwise unequal. for (i, prime1) in primes.iter().enumerate() { for prime2 in primes.iter().take(i) { if prime1 == prime2 { return Err(Error::InvalidPrime); } } } let n = compute_modulus(&primes); let d = compute_private_exponent_euler_totient(&primes, &public_exponent)?; Self::from_components(n, public_exponent, d, primes) } /// Get the public key from the private key, cloning `n` and `e`. /// /// Generally this is not needed since `RsaPrivateKey` implements the `PublicKey` trait, /// but it can occasionally be useful to discard the private information entirely. pub fn to_public_key(&self) -> RsaPublicKey { self.pubkey_components.clone() } /// Performs some calculations to speed up private key operations. pub fn precompute(&mut self) -> Result<()> { if self.precomputed.is_some() { return Ok(()); } let dp = &self.d % (&self.primes[0] - BigUint::one()); let dq = &self.d % (&self.primes[1] - BigUint::one()); let qinv = self.primes[1] .clone() .mod_inverse(&self.primes[0]) .ok_or(Error::InvalidPrime)?; let mut r: BigUint = &self.primes[0] * &self.primes[1]; let crt_values: Vec = { let mut values = Vec::with_capacity(self.primes.len() - 2); for prime in &self.primes[2..] { let res = CrtValue { exp: BigInt::from_biguint(Plus, &self.d % (prime - BigUint::one())), r: BigInt::from_biguint(Plus, r.clone()), coeff: BigInt::from_biguint( Plus, r.clone() .mod_inverse(prime) .ok_or(Error::InvalidCoefficient)? .to_biguint() .unwrap(), ), }; r *= prime; values.push(res); } values }; self.precomputed = Some(PrecomputedValues { dp, dq, qinv, crt_values, }); Ok(()) } /// Clears precomputed values by setting to None pub fn clear_precomputed(&mut self) { self.precomputed = None; } /// Compute CRT coefficient: `(1/q) mod p`. pub fn crt_coefficient(&self) -> Option { (&self.primes[1]).mod_inverse(&self.primes[0])?.to_biguint() } /// Performs basic sanity checks on the key. /// Returns `Ok(())` if everything is good, otherwise an appropriate error. pub fn validate(&self) -> Result<()> { check_public(self)?; // Check that Πprimes == n. let mut m = BigUint::one(); for prime in &self.primes { // Any primes ≤ 1 will cause divide-by-zero panics later. if *prime < BigUint::one() { return Err(Error::InvalidPrime); } m *= prime; } if m != self.pubkey_components.n { return Err(Error::InvalidModulus); } // Check that de ≡ 1 mod p-1, for each prime. // This implies that e is coprime to each p-1 as e has a multiplicative // inverse. Therefore e is coprime to lcm(p-1,q-1,r-1,...) = // exponent(ℤ/nℤ). It also implies that a^de ≡ a mod p as a^(p-1) ≡ 1 // mod p. Thus a^de ≡ a mod n for all a coprime to n, as required. let mut de = self.e().clone(); de *= self.d.clone(); for prime in &self.primes { let congruence: BigUint = &de % (prime - BigUint::one()); if !congruence.is_one() { return Err(Error::InvalidExponent); } } Ok(()) } /// Decrypt the given message. pub fn decrypt(&self, padding: P, ciphertext: &[u8]) -> Result> { padding.decrypt(Option::<&mut DummyRng>::None, self, ciphertext) } /// Decrypt the given message. /// /// Uses `rng` to blind the decryption process. pub fn decrypt_blinded( &self, rng: &mut R, padding: P, ciphertext: &[u8], ) -> Result> { padding.decrypt(Some(rng), self, ciphertext) } /// Sign the given digest. pub fn sign(&self, padding: S, digest_in: &[u8]) -> Result> { padding.sign(Option::<&mut DummyRng>::None, self, digest_in) } /// Sign the given digest using the provided `rng`, which is used in the /// following ways depending on the [`SignatureScheme`]: /// /// - [`Pkcs1v15Sign`][`crate::Pkcs1v15Sign`] padding: uses the RNG /// to mask the private key operation with random blinding, which helps /// mitigate sidechannel attacks. /// - [`Pss`][`crate::Pss`] always requires randomness. Use /// [`Pss::new`][`crate::Pss::new`] for a standard RSASSA-PSS signature, or /// [`Pss::new_blinded`][`crate::Pss::new_blinded`] for RSA-BSSA blind /// signatures. pub fn sign_with_rng( &self, rng: &mut R, padding: S, digest_in: &[u8], ) -> Result> { padding.sign(Some(rng), self, digest_in) } } impl PrivateKeyParts for RsaPrivateKey { fn d(&self) -> &BigUint { &self.d } fn primes(&self) -> &[BigUint] { &self.primes } fn dp(&self) -> Option<&BigUint> { self.precomputed.as_ref().map(|p| &p.dp) } fn dq(&self) -> Option<&BigUint> { self.precomputed.as_ref().map(|p| &p.dq) } fn qinv(&self) -> Option<&BigInt> { self.precomputed.as_ref().map(|p| &p.qinv) } fn crt_values(&self) -> Option<&[CrtValue]> { /* for some reason the standard self.precomputed.as_ref().map() doesn't work */ if let Some(p) = &self.precomputed { Some(p.crt_values.as_slice()) } else { None } } } /// Check that the public key is well formed and has an exponent within acceptable bounds. #[inline] pub fn check_public(public_key: &impl PublicKeyParts) -> Result<()> { check_public_with_max_size(public_key, RsaPublicKey::MAX_SIZE) } /// Check that the public key is well formed and has an exponent within acceptable bounds. #[inline] fn check_public_with_max_size(public_key: &impl PublicKeyParts, max_size: usize) -> Result<()> { if public_key.n().bits() > max_size { return Err(Error::ModulusTooLarge); } let e = public_key .e() .to_u64() .ok_or(Error::PublicExponentTooLarge)?; if public_key.e() >= public_key.n() || public_key.n().is_even() { return Err(Error::InvalidModulus); } if public_key.e().is_even() { return Err(Error::InvalidExponent); } if e < RsaPublicKey::MIN_PUB_EXPONENT { return Err(Error::PublicExponentTooSmall); } if e > RsaPublicKey::MAX_PUB_EXPONENT { return Err(Error::PublicExponentTooLarge); } Ok(()) } #[cfg(test)] mod tests { use super::*; use crate::algorithms::rsa::{rsa_decrypt_and_check, rsa_encrypt}; use hex_literal::hex; use num_traits::{FromPrimitive, ToPrimitive}; use pkcs8::DecodePrivateKey; use rand_chacha::{rand_core::SeedableRng, ChaCha8Rng}; #[test] fn test_from_into() { let private_key = RsaPrivateKey { pubkey_components: RsaPublicKey { n: BigUint::from_u64(100).unwrap(), e: BigUint::from_u64(200).unwrap(), }, d: BigUint::from_u64(123).unwrap(), primes: vec![], precomputed: None, }; let public_key: RsaPublicKey = private_key.into(); assert_eq!(public_key.n().to_u64(), Some(100)); assert_eq!(public_key.e().to_u64(), Some(200)); } fn test_key_basics(private_key: &RsaPrivateKey) { private_key.validate().expect("invalid private key"); assert!( private_key.d() < private_key.n(), "private exponent too large" ); let pub_key: RsaPublicKey = private_key.clone().into(); let m = BigUint::from_u64(42).expect("invalid 42"); let c = rsa_encrypt(&pub_key, &m).expect("encryption successfull"); let m2 = rsa_decrypt_and_check::(private_key, None, &c) .expect("unable to decrypt without blinding"); assert_eq!(m, m2); let mut rng = ChaCha8Rng::from_seed([42; 32]); let m3 = rsa_decrypt_and_check(private_key, Some(&mut rng), &c) .expect("unable to decrypt with blinding"); assert_eq!(m, m3); } macro_rules! key_generation { ($name:ident, $multi:expr, $size:expr) => { #[test] fn $name() { let mut rng = ChaCha8Rng::from_seed([42; 32]); let exp = BigUint::from_u64(RsaPrivateKey::EXP).expect("invalid static exponent"); for _ in 0..10 { let components = generate_multi_prime_key_with_exp(&mut rng, $multi, $size, &exp).unwrap(); let private_key = RsaPrivateKey::from_components( components.n, components.e, components.d, components.primes, ) .unwrap(); assert_eq!(private_key.n().bits(), $size); test_key_basics(&private_key); } } }; } key_generation!(key_generation_128, 2, 128); key_generation!(key_generation_1024, 2, 1024); key_generation!(key_generation_multi_3_256, 3, 256); key_generation!(key_generation_multi_4_64, 4, 64); key_generation!(key_generation_multi_5_64, 5, 64); key_generation!(key_generation_multi_8_576, 8, 576); key_generation!(key_generation_multi_16_1024, 16, 1024); #[test] fn test_negative_decryption_value() { let private_key = RsaPrivateKey::from_components( BigUint::from_bytes_le(&[ 99, 192, 208, 179, 0, 220, 7, 29, 49, 151, 75, 107, 75, 73, 200, 180, ]), BigUint::from_bytes_le(&[1, 0, 1]), BigUint::from_bytes_le(&[ 81, 163, 254, 144, 171, 159, 144, 42, 244, 133, 51, 249, 28, 12, 63, 65, ]), vec![ BigUint::from_bytes_le(&[105, 101, 60, 173, 19, 153, 3, 192]), BigUint::from_bytes_le(&[235, 65, 160, 134, 32, 136, 6, 241]), ], ) .unwrap(); for _ in 0..1000 { test_key_basics(&private_key); } } #[test] #[cfg(feature = "serde")] fn test_serde() { use rand_chacha::{rand_core::SeedableRng, ChaCha8Rng}; use serde_test::{assert_tokens, Token}; let mut rng = ChaCha8Rng::from_seed([42; 32]); let priv_key = RsaPrivateKey::new(&mut rng, 64).expect("failed to generate key"); let priv_tokens = [ Token::Struct { name: "RsaPrivateKey", len: 3, }, Token::Str("pubkey_components"), Token::Struct { name: "RsaPublicKey", len: 2, }, Token::Str("n"), Token::Seq { len: Some(2) }, Token::U32(3814409919), Token::U32(3429654832), Token::SeqEnd, Token::Str("e"), Token::Seq { len: Some(1) }, Token::U32(65537), Token::SeqEnd, Token::StructEnd, Token::Str("d"), Token::Seq { len: Some(2) }, Token::U32(1482162201), Token::U32(1675500232), Token::SeqEnd, Token::Str("primes"), Token::Seq { len: Some(2) }, Token::Seq { len: Some(1) }, Token::U32(4133289821), Token::SeqEnd, Token::Seq { len: Some(1) }, Token::U32(3563808971), Token::SeqEnd, Token::SeqEnd, Token::StructEnd, ]; assert_tokens(&priv_key, &priv_tokens); let priv_tokens = [ Token::Struct { name: "RsaPublicKey", len: 2, }, Token::Str("n"), Token::Seq { len: Some(2) }, Token::U32(3814409919), Token::U32(3429654832), Token::SeqEnd, Token::Str("e"), Token::Seq { len: Some(1) }, Token::U32(65537), Token::SeqEnd, Token::StructEnd, ]; assert_tokens(&RsaPublicKey::from(priv_key), &priv_tokens); } #[test] fn invalid_coeff_private_key_regression() { use base64ct::{Base64, Encoding}; let n = Base64::decode_vec("wC8GyQvTCZOK+iiBR5fGQCmzRCTWX9TQ3aRG5gGFk0wB6EFoLMAyEEqeG3gS8xhAm2rSWYx9kKufvNat3iWlbSRVqkcbpVAYlj2vTrpqDpJl+6u+zxFYoUEBevlJJkAhl8EuCccOA30fVpcfRvXPTtvRd3yFT9E9EwZljtgSI02w7gZwg7VIxaGeajh5Euz6ZVQZ+qNRKgXrRC7gPRqVyI6Dt0Jc+Su5KBGNn0QcPDzOahWha1ieaeMkFisZ9mdpsJoZ4tw5eicLaUomKzALHXQVt+/rcZSrCd6/7uUo11B/CYBM4UfSpwXaL88J9AE6A5++no9hmJzaF2LLp+Qwx4yY3j9TDutxSAjsraxxJOGZ3XyA9nG++Ybt3cxZ5fP7ROjxCfROBmVv5dYn0O9OBIqYeCH6QraNpZMadlLNIhyMv8Y+P3r5l/PaK4VJaEi5pPosnEPawp0W0yZDzmjk2z1LthaRx0aZVrAjlH0Rb/6goLUQ9qu1xsDtQVVpN4A89ZUmtTWORnnJr0+595eHHxssd2gpzqf4bPjNITdAEuOCCtpvyi4ls23zwuzryUYjcUOEnsXNQ+DrZpLKxdtsD/qNV/j1hfeyBoPllC3cV+6bcGOFcVGbjYqb+Kw1b0+jL69RSKQqgmS+qYqr8c48nDRxyq3QXhR8qtzUwBFSLVk=").unwrap(); let e = Base64::decode_vec("AQAB").unwrap(); let d = Base64::decode_vec("qQazSQ+FRN7nVK1bRsROMRB8AmsDwLVEHivlz1V3Td2Dr+oW3YUMgxedhztML1IdQJPq/ad6qErJ6yRFNySVIjDaxzBTOEoB1eHa1btOnBJWb8rVvvjaorixvJ6Tn3i4EuhsvVy9DoR1k4rGj3qSIiFjUVvLRDAbLyhpGgEfsr0Z577yJmTC5E8JLRMOKX8Tmxsk3jPVpsgd65Hu1s8S/ZmabwuHCf9SkdMeY/1bd/9i7BqqJeeDLE4B5x1xcC3z3scqDUTzqGO+vZPhjgprPDRlBamVwgenhr7KwCn8iaLamFinRVwOAag8BeBqOJj7lURiOsKQa9FIX1kdFUS1QMQxgtPycLjkbvCJjriqT7zWKsmJ7l8YLs6Wmm9/+QJRwNCEVdMTXKfCP1cJjudaiskEQThfUldtgu8gUDNYbQ/Filb2eKfiX4h1TiMxZqUZHVZyb9nShbQoXJ3vj/MGVF0QM8TxhXM8r2Lv9gDYU5t9nQlUMLhs0jVjai48jHABbFNyH3sEcOmJOIwJrCXw1dzG7AotwyaEVUHOmL04TffmwCFfnyrLjbFgnyOeoyIIBYjcY7QFRm/9nupXMTH5hZ2qrHfCJIp0KK4tNBdQqmnHapFl5l6Le1s4qBS5bEIzjitobLvAFm9abPlDGfxmY6mlrMK4+nytwF9Ct7wc1AE=").unwrap(); let primes = vec![ Base64::decode_vec("9kQWEAzsbzOcdPa+s5wFfw4XDd7bB1q9foZ31b1+TNjGNxbSBCFlDF1q98vwpV6nM8bWDh/wtbNoETSQDgpEnYOQ26LWEw6YY1+q1Q2GGEFceYUf+Myk8/vTc8TN6Zw0bKZBWy10Qo8h7xk4JpzuI7NcxvjJYTkS9aErFxi3vVH0aiZC0tmfaCqr8a2rJxyVwqreRpOjwAWrotMsf2wGsF4ofx5ScoFy5GB5fJkkdOrW1LyTvZAUCX3cstPr19+TNC5zZOk7WzZatnCkN5H5WzalWtZuu0oVL205KPOa3R8V2yv5e6fm0v5fTmqSuvjmaMJLXCN4QJkmIzojO99ckQ==").unwrap(), Base64::decode_vec("x8exdMjVA2CiI+Thx7loHtVcevoeE2sZ7btRVAvmBqo+lkHwxb7FHRnWvuj6eJSlD2f0T50EewIhhiW3R9BmktCk7hXjbSCnC1u9Oxc1IAUm/7azRqyfCMx43XhLxpD+xkBCpWkKDLxGczsRwTuaP3lKS3bSdBrNlGmdblubvVBIq4YZ2vXVlnYtza0cS+dgCK7BGTqUsrCUd/ZbIvwcwZkZtpkhj1KQfto9X/0OMurBzAqbkeq1cyRHXHkOfN/qbUIIRqr9Ii7Eswf9Vk8xp2O1Nt8nzcYS9PFD12M5eyaeFEkEYfpNMNGuTzp/31oqVjbpoCxS6vuWAZyADxhISQ==").unwrap(), Base64::decode_vec("is7d0LY4HoXszlC2NO7gejkq7XqL4p1W6hZJPYTNx+r37t1CC2n3Vvzg6kNdpRixDhIpXVTLjN9O7UO/XuqSumYKJIKoP52eb4Tg+a3hw5Iz2Zsb5lUTNSLgkQSBPAf71LHxbL82JL4g1nBUog8ae60BwnVArThKY4EwlJguGNw09BAU4lwf6csDl/nX2vfVwiAloYpeZkHL+L8m+bueGZM5KE2jEz+7ztZCI+T+E5i69rZEYDjx0lfLKlEhQlCW3HbCPELqXgNJJkRfi6MP9kXa9lSfnZmoT081RMvqonB/FUa4HOcKyCrw9XZEtnbNCIdbitfDVEX+pSSD7596wQ==").unwrap(), Base64::decode_vec("GPs0injugfycacaeIP5jMa/WX55VEnKLDHom4k6WlfDF4L4gIGoJdekcPEUfxOI5faKvHyFwRP1wObkPoRBDM0qZxRfBl4zEtpvjHrd5MibSyJkM8+J0BIKk/nSjbRIGeb3hV5O56PvGB3S0dKhCUnuVObiC+ne7izplsD4OTG70l1Yud33UFntyoMxrxGYLUSqhBMmZfHquJg4NOWOzKNY/K+EcHDLj1Kjvkcgv9Vf7ocsVxvpFdD9uGPceQ6kwRDdEl6mb+6FDgWuXVyqR9+904oanEIkbJ7vfkthagLbEf57dyG6nJlqh5FBZWxGIR72YGypPuAh7qnnqXXjY2Q==").unwrap(), Base64::decode_vec("CUWC+hRWOT421kwRllgVjy6FYv6jQUcgDNHeAiYZnf5HjS9iK2ki7v8G5dL/0f+Yf+NhE/4q8w4m8go51hACrVpP1p8GJDjiT09+RsOzITsHwl+ceEKoe56ZW6iDHBLlrNw5/MtcYhKpjNU9KJ2udm5J/c9iislcjgckrZG2IB8ADgXHMEByZ5DgaMl4AKZ1Gx8/q6KftTvmOT5rNTMLi76VN5KWQcDWK/DqXiOiZHM7Nr4dX4me3XeRgABJyNR8Fqxj3N1+HrYLe/zs7LOaK0++F9Ul3tLelhrhsvLxei3oCZkF9A/foD3on3luYA+1cRcxWpSY3h2J4/22+yo4+Q==").unwrap(), ]; let res = RsaPrivateKey::from_components( BigUint::from_bytes_be(&n), BigUint::from_bytes_be(&e), BigUint::from_bytes_be(&d), primes.iter().map(|p| BigUint::from_bytes_be(p)).collect(), ); assert_eq!(res, Err(Error::InvalidModulus)); } #[test] fn reject_oversized_private_key() { // -----BEGIN PUBLIC KEY----- // MIIEIjANBgkqhkiG9w0BAQEFAAOCBA8AMIIECgKCBAEAkMBiB8qsNVXAsJR6Xoto // H1r2rtZl/xzUK2tIfy99aPE489u+5tLxCQhQf+a89158vSDpr2/xwgK8w9u0Xpu2 // m7XRKjVMS0Y6UIINFoeTc87rVXT92Scr47kNVcGmSFXez4BSDpS+LKpWwXN+0AQu // +cmcfdtsx2862iEbqQvq4PwKGQJOdOR0yldH8O4yeJK/buvIOXRHjb++vtQND/xi // bFGAcd9WJqvaOG7tclhbZ277mbO6ER+y9Lj7AyO8ywybWqNeHaVPHMysPhT7HUWI // 17m59i1OpuVwwEnvzDQQEUf9d5hUmkLYb5qQzuf6Ddnx/04QJCKAgkhyr9CXgnV6 // vEZ3PKtpicCHRxk7eqTEmgBlgwqH5vflRFV1iywQMXJnuRhzWOQaXl/vb8v4HIvF // 4TatEZKqfzpbyScLIiYbPEAhHXKdZMd2zY8hkSbicifePApAZmuNpAxxJDZzphh7 // r4lD6t8MPT/RUAdtrZfihqaBhduFI6YeVIy6emg05M6YWvlUyer7nYGaPRS1JqD4 // 0v7xOtme5I8Qw6APiFPXhTqBK3occr7TgGb3V3lpC8Eq+esNHrji98R1fITkFXJW // KdFcTWjBghPxiobUzMCFUrPIDJcWXeBzrARAryU+hXjEiFfzluXrps0B7RJQ/rLD // LXeTn4vovUeHQVHa7YfoyWMy9pfqeVC+56LBK7SEIAvL0I3lrq5vIv+ZIuOAdbVg // JiRy8DneCOk2LP3RnA8M0HSevYW93DiC+4h/l4ntjjiOfi6yRVOZ8WbVyXZ/83j4 // 6+pGWgvi0uMyb+btgOXjBQv7bGqdyHMc5Lqk5bF7ExETx51vKQMYCV4351caS6aX // q16lYZATHgbTADEAZHdroDMJB+HMQaze9O6qU5ZO8wxxAjw89xry0dnoOQD/yA4H // 7CRCo9vVDpV2hqIvHY9RI2T7cek28kmQpKvNvvK+ovmM138dHKViWULHk0fBRt7m // 4wQ+tiL2PmJ/Tr8g1gVhM6S9D1XdE9z0KeDnODCWn1Q8sx2G2ah4ynnYQURDWcwO // McAoP6bdJ7cCt+4F2tEsMPf4S/EwlnjvuNoQjvztxCPahYe9EnyggtQXyHJveIn7 // gDJsP6b93VB6x4QbLy5ch4DUhqDWginuKVeo7CTgDkq03j/IEaS1BHwreSDQceny // +bYWONwV+4TMpGytKOHvU5288kmHbyZHdXuaXk8LLqbnqr30fa6Cbp4llCi9sH5a // Kmi5jxQfVTe+elkMs7oVsLsVgkZS6NqPcOuEckAFijNqG223+IJoqvifCzO5Bdcs // JTOLE+YaUYc8LUJwIaPykgcXmtMvQjeT8MCQ3aAlzkHfDpSvvICrXtqbGiaKolU6 // mQIDAQAB // -----END PUBLIC KEY----- let n = BigUint::from_bytes_be(&hex!( " 90c06207caac3555c0b0947a5e8b681f5af6aed665ff1cd42b6b487f2f7d68f1 38f3dbbee6d2f10908507fe6bcf75e7cbd20e9af6ff1c202bcc3dbb45e9bb69b b5d12a354c4b463a50820d16879373ceeb5574fdd9272be3b90d55c1a64855de cf80520e94be2caa56c1737ed0042ef9c99c7ddb6cc76f3ada211ba90beae0fc 0a19024e74e474ca5747f0ee327892bf6eebc83974478dbfbebed40d0ffc626c 518071df5626abda386eed72585b676efb99b3ba111fb2f4b8fb0323bccb0c9b 5aa35e1da54f1cccac3e14fb1d4588d7b9b9f62d4ea6e570c049efcc34101147 fd7798549a42d86f9a90cee7fa0dd9f1ff4e10242280824872afd09782757abc 46773cab6989c08747193b7aa4c49a0065830a87e6f7e54455758b2c10317267 b9187358e41a5e5fef6fcbf81c8bc5e136ad1192aa7f3a5bc9270b22261b3c40 211d729d64c776cd8f219126e27227de3c0a40666b8da40c71243673a6187baf 8943eadf0c3d3fd150076dad97e286a68185db8523a61e548cba7a6834e4ce98 5af954c9eafb9d819a3d14b526a0f8d2fef13ad99ee48f10c3a00f8853d7853a 812b7a1c72bed38066f75779690bc12af9eb0d1eb8e2f7c4757c84e415725629 d15c4d68c18213f18a86d4ccc08552b3c80c97165de073ac0440af253e8578c4 8857f396e5eba6cd01ed1250feb2c32d77939f8be8bd47874151daed87e8c963 32f697ea7950bee7a2c12bb484200bcbd08de5aeae6f22ff9922e38075b56026 2472f039de08e9362cfdd19c0f0cd0749ebd85bddc3882fb887f9789ed8e388e 7e2eb2455399f166d5c9767ff378f8ebea465a0be2d2e3326fe6ed80e5e3050b fb6c6a9dc8731ce4baa4e5b17b131113c79d6f290318095e37e7571a4ba697ab 5ea56190131e06d300310064776ba0330907e1cc41acdef4eeaa53964ef30c71 023c3cf71af2d1d9e83900ffc80e07ec2442a3dbd50e957686a22f1d8f512364 fb71e936f24990a4abcdbef2bea2f98cd77f1d1ca5625942c79347c146dee6e3 043eb622f63e627f4ebf20d6056133a4bd0f55dd13dcf429e0e73830969f543c b31d86d9a878ca79d841444359cc0e31c0283fa6dd27b702b7ee05dad12c30f7 f84bf1309678efb8da108efcedc423da8587bd127ca082d417c8726f7889fb80 326c3fa6fddd507ac7841b2f2e5c8780d486a0d68229ee2957a8ec24e00e4ab4 de3fc811a4b5047c2b7920d071e9f2f9b61638dc15fb84cca46cad28e1ef539d bcf249876f2647757b9a5e4f0b2ea6e7aabdf47dae826e9e259428bdb07e5a2a 68b98f141f5537be7a590cb3ba15b0bb15824652e8da8f70eb847240058a336a 1b6db7f88268aaf89f0b33b905d72c25338b13e61a51873c2d427021a3f29207 179ad32f423793f0c090dda025ce41df0e94afbc80ab5eda9b1a268aa2553a99" )); let e = BigUint::from_u64(65537).unwrap(); assert_eq!( RsaPublicKey::new(n, e).err().unwrap(), Error::ModulusTooLarge ); } #[test] fn build_key_from_primes() { const RSA_2048_PRIV_DER: &[u8] = include_bytes!("../tests/examples/pkcs8/rsa2048-priv.der"); let ref_key = RsaPrivateKey::from_pkcs8_der(RSA_2048_PRIV_DER).unwrap(); assert_eq!(ref_key.validate(), Ok(())); let primes = ref_key.primes().to_vec(); let exp = ref_key.e().clone(); let key = RsaPrivateKey::from_primes(primes, exp).expect("failed to import key from primes"); assert_eq!(key.validate(), Ok(())); assert_eq!(key.n(), ref_key.n()); assert_eq!(key.dp(), ref_key.dp()); assert_eq!(key.dq(), ref_key.dq()); assert_eq!(key.d(), ref_key.d()); } #[test] fn build_key_from_p_q() { const RSA_2048_SP800_PRIV_DER: &[u8] = include_bytes!("../tests/examples/pkcs8/rsa2048-sp800-56b-priv.der"); let ref_key = RsaPrivateKey::from_pkcs8_der(RSA_2048_SP800_PRIV_DER).unwrap(); assert_eq!(ref_key.validate(), Ok(())); let primes = ref_key.primes().to_vec(); let exp = ref_key.e().clone(); let key = RsaPrivateKey::from_p_q(primes[0].clone(), primes[1].clone(), exp) .expect("failed to import key from primes"); assert_eq!(key.validate(), Ok(())); assert_eq!(key.n(), ref_key.n()); assert_eq!(key.dp(), ref_key.dp()); assert_eq!(key.dq(), ref_key.dq()); assert_eq!(key.d(), ref_key.d()); } } rsa-0.9.7/src/lib.rs000064400000000000000000000202061046102023000123310ustar 00000000000000#![cfg_attr(not(test), no_std)] #![cfg_attr(docsrs, feature(doc_auto_cfg))] #![doc = include_str!("../README.md")] #![doc(html_logo_url = "https://raw.githubusercontent.com/RustCrypto/meta/master/logo_small.png")] #![warn(missing_docs)] //! # Supported algorithms //! //! This crate supports several schemes described in [RFC8017]: //! //! - [OAEP encryption scheme](#oaep-encryption) //! - [PKCS#1 v1.5 encryption scheme](#pkcs1-v15-encryption) //! - [PKCS#1 v1.5 signature scheme](#pkcs1-v15-signatures) //! - [PSS signature scheme](#pss-signatures) //! //! These schemes are described below. //! //! # Usage //! //! ## OAEP encryption //! //! Note: requires `sha2` feature of `rsa` crate is enabled. //! #![cfg_attr(feature = "sha2", doc = "```")] #![cfg_attr(not(feature = "sha2"), doc = "```ignore")] //! use rsa::{RsaPrivateKey, RsaPublicKey, Oaep, sha2::Sha256}; //! //! let mut rng = rand::thread_rng(); //! //! let bits = 2048; //! let private_key = RsaPrivateKey::new(&mut rng, bits).expect("failed to generate a key"); //! let public_key = RsaPublicKey::from(&private_key); //! //! // Encrypt //! let data = b"hello world"; //! let padding = Oaep::new::(); //! let enc_data = public_key.encrypt(&mut rng, padding, &data[..]).expect("failed to encrypt"); //! assert_ne!(&data[..], &enc_data[..]); //! //! // Decrypt //! let padding = Oaep::new::(); //! let dec_data = private_key.decrypt(padding, &enc_data).expect("failed to decrypt"); //! assert_eq!(&data[..], &dec_data[..]); //! ``` //! //! ## PKCS#1 v1.5 encryption //! ``` //! use rsa::{RsaPrivateKey, RsaPublicKey, Pkcs1v15Encrypt}; //! //! let mut rng = rand::thread_rng(); //! //! let bits = 2048; //! let private_key = RsaPrivateKey::new(&mut rng, bits).expect("failed to generate a key"); //! let public_key = RsaPublicKey::from(&private_key); //! //! // Encrypt //! let data = b"hello world"; //! let enc_data = public_key.encrypt(&mut rng, Pkcs1v15Encrypt, &data[..]).expect("failed to encrypt"); //! assert_ne!(&data[..], &enc_data[..]); //! //! // Decrypt //! let dec_data = private_key.decrypt(Pkcs1v15Encrypt, &enc_data).expect("failed to decrypt"); //! assert_eq!(&data[..], &dec_data[..]); //! ``` //! //! ## PKCS#1 v1.5 signatures //! //! Note: requires `sha2` feature of `rsa` crate is enabled. //! #![cfg_attr(feature = "sha2", doc = "```")] #![cfg_attr(not(feature = "sha2"), doc = "```ignore")] //! use rsa::RsaPrivateKey; //! use rsa::pkcs1v15::{SigningKey, VerifyingKey}; //! use rsa::signature::{Keypair, RandomizedSigner, SignatureEncoding, Verifier}; //! use rsa::sha2::{Digest, Sha256}; //! //! let mut rng = rand::thread_rng(); //! //! let bits = 2048; //! let private_key = RsaPrivateKey::new(&mut rng, bits).expect("failed to generate a key"); //! let signing_key = SigningKey::::new(private_key); //! let verifying_key = signing_key.verifying_key(); //! //! // Sign //! let data = b"hello world"; //! let signature = signing_key.sign_with_rng(&mut rng, data); //! assert_ne!(signature.to_bytes().as_ref(), data.as_slice()); //! //! // Verify //! verifying_key.verify(data, &signature).expect("failed to verify"); //! ``` //! //! ## PSS signatures //! //! Note: requires `sha2` feature of `rsa` crate is enabled. //! #![cfg_attr(feature = "sha2", doc = "```")] #![cfg_attr(not(feature = "sha2"), doc = "```ignore")] //! use rsa::RsaPrivateKey; //! use rsa::pss::{BlindedSigningKey, VerifyingKey}; //! use rsa::signature::{Keypair,RandomizedSigner, SignatureEncoding, Verifier}; //! use rsa::sha2::{Digest, Sha256}; //! //! let mut rng = rand::thread_rng(); //! //! let bits = 2048; //! let private_key = RsaPrivateKey::new(&mut rng, bits).expect("failed to generate a key"); //! let signing_key = BlindedSigningKey::::new(private_key); //! let verifying_key = signing_key.verifying_key(); //! //! // Sign //! let data = b"hello world"; //! let signature = signing_key.sign_with_rng(&mut rng, data); //! assert_ne!(signature.to_bytes().as_ref(), data); //! //! // Verify //! verifying_key.verify(data, &signature).expect("failed to verify"); //! ``` //! //! ## PKCS#1 RSA Key Encoding //! //! PKCS#1 supports a legacy format for encoding RSA keys as binary (DER) or //! text (PEM) data. //! //! You can recognize PEM encoded PKCS#1 keys because they have "RSA * KEY" in //! the type label, e.g.: //! //! ```text //! -----BEGIN RSA PRIVATE KEY----- //! ``` //! //! Most modern applications use the newer PKCS#8 format instead (see below). //! //! The following traits can be used to decode/encode [`RsaPrivateKey`] and //! [`RsaPublicKey`] as PKCS#1. Note that [`pkcs1`] is re-exported from the //! toplevel of the `rsa` crate: //! //! - [`pkcs1::DecodeRsaPrivateKey`]: decode RSA private keys from PKCS#1 //! - [`pkcs1::EncodeRsaPrivateKey`]: encode RSA private keys to PKCS#1 //! - [`pkcs1::DecodeRsaPublicKey`]: decode RSA public keys from PKCS#1 //! - [`pkcs1::EncodeRsaPublicKey`]: encode RSA public keys to PKCS#1 //! //! ### Example //! //! ``` //! # fn main() -> Result<(), Box> { //! # #[cfg(all(feature = "pem", feature = "std"))] //! # { //! use rsa::{RsaPublicKey, pkcs1::DecodeRsaPublicKey}; //! //! let pem = "-----BEGIN RSA PUBLIC KEY----- //! MIIBCgKCAQEAtsQsUV8QpqrygsY+2+JCQ6Fw8/omM71IM2N/R8pPbzbgOl0p78MZ //! GsgPOQ2HSznjD0FPzsH8oO2B5Uftws04LHb2HJAYlz25+lN5cqfHAfa3fgmC38Ff //! wBkn7l582UtPWZ/wcBOnyCgb3yLcvJrXyrt8QxHJgvWO23ITrUVYszImbXQ67YGS //! 0YhMrbixRzmo2tpm3JcIBtnHrEUMsT0NfFdfsZhTT8YbxBvA8FdODgEwx7u/vf3J //! 9qbi4+Kv8cvqyJuleIRSjVXPsIMnoejIn04APPKIjpMyQdnWlby7rNyQtE4+CV+j //! cFjqJbE/Xilcvqxt6DirjFCvYeKYl1uHLwIDAQAB //! -----END RSA PUBLIC KEY-----"; //! //! let public_key = RsaPublicKey::from_pkcs1_pem(pem)?; //! # } //! # Ok(()) //! # } //! ``` //! //! ## PKCS#8 RSA Key Encoding //! //! PKCS#8 is a private key format with support for multiple algorithms. //! Like PKCS#1, it can be encoded as binary (DER) or text (PEM). //! //! You can recognize PEM encoded PKCS#8 keys because they *don't* have //! an algorithm name in the type label, e.g.: //! //! ```text //! -----BEGIN PRIVATE KEY----- //! ``` //! //! The following traits can be used to decode/encode [`RsaPrivateKey`] and //! [`RsaPublicKey`] as PKCS#8. Note that [`pkcs8`] is re-exported from the //! toplevel of the `rsa` crate: //! //! - [`pkcs8::DecodePrivateKey`]: decode private keys from PKCS#8 //! - [`pkcs8::EncodePrivateKey`]: encode private keys to PKCS#8 //! - [`pkcs8::DecodePublicKey`]: decode public keys from PKCS#8 //! - [`pkcs8::EncodePublicKey`]: encode public keys to PKCS#8 //! //! ### Example //! //! ``` //! # fn main() -> Result<(), Box> { //! # #[cfg(all(feature = "pem", feature = "std"))] //! # { //! use rsa::{RsaPublicKey, pkcs8::DecodePublicKey}; //! //! let pem = "-----BEGIN PUBLIC KEY----- //! MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAtsQsUV8QpqrygsY+2+JC //! Q6Fw8/omM71IM2N/R8pPbzbgOl0p78MZGsgPOQ2HSznjD0FPzsH8oO2B5Uftws04 //! LHb2HJAYlz25+lN5cqfHAfa3fgmC38FfwBkn7l582UtPWZ/wcBOnyCgb3yLcvJrX //! yrt8QxHJgvWO23ITrUVYszImbXQ67YGS0YhMrbixRzmo2tpm3JcIBtnHrEUMsT0N //! fFdfsZhTT8YbxBvA8FdODgEwx7u/vf3J9qbi4+Kv8cvqyJuleIRSjVXPsIMnoejI //! n04APPKIjpMyQdnWlby7rNyQtE4+CV+jcFjqJbE/Xilcvqxt6DirjFCvYeKYl1uH //! LwIDAQAB //! -----END PUBLIC KEY-----"; //! //! let public_key = RsaPublicKey::from_public_key_pem(pem)?; //! # } //! # Ok(()) //! # } //! ``` //! //! [RFC8017]: https://datatracker.ietf.org/doc/html/rfc8017#section-8.1 //! // TODO(tarcieri): figure out why rustdoc isn't rendering these links correctly //! [`pkcs8::DecodePublicKey`]: https://docs.rs/pkcs8/latest/pkcs8/trait.DecodePublicKey.html //! [`pkcs8::EncodePublicKey`]: https://docs.rs/pkcs8/latest/pkcs8/trait.EncodePublicKey.html #[cfg(doctest)] pub struct ReadmeDoctests; #[macro_use] extern crate alloc; #[cfg(feature = "std")] extern crate std; pub use num_bigint::BigUint; pub use rand_core; pub use signature; mod algorithms; pub mod errors; pub mod oaep; pub mod pkcs1v15; pub mod pss; pub mod traits; mod dummy_rng; mod encoding; mod key; pub use pkcs1; pub use pkcs8; #[cfg(feature = "sha2")] pub use sha2; pub use crate::{ errors::{Error, Result}, key::{RsaPrivateKey, RsaPublicKey}, oaep::Oaep, pkcs1v15::{Pkcs1v15Encrypt, Pkcs1v15Sign}, pss::Pss, traits::keys::CrtValue, }; #[cfg(feature = "hazmat")] pub mod hazmat; rsa-0.9.7/src/oaep/decrypting_key.rs000064400000000000000000000044461046102023000155370ustar 00000000000000use super::decrypt_digest; use crate::{ dummy_rng::DummyRng, traits::{Decryptor, RandomizedDecryptor}, Result, RsaPrivateKey, }; use alloc::{ string::{String, ToString}, vec::Vec, }; use core::marker::PhantomData; use digest::{Digest, FixedOutputReset}; use rand_core::CryptoRngCore; use zeroize::ZeroizeOnDrop; /// Decryption key for PKCS#1 v1.5 decryption as described in [RFC8017 § 7.1]. /// /// [RFC8017 § 7.1]: https://datatracker.ietf.org/doc/html/rfc8017#section-7.1 #[derive(Debug, Clone)] pub struct DecryptingKey where D: Digest, MGD: Digest + FixedOutputReset, { inner: RsaPrivateKey, label: Option, phantom: PhantomData, mg_phantom: PhantomData, } impl DecryptingKey where D: Digest, MGD: Digest + FixedOutputReset, { /// Create a new verifying key from an RSA public key. pub fn new(key: RsaPrivateKey) -> Self { Self { inner: key, label: None, phantom: Default::default(), mg_phantom: Default::default(), } } /// Create a new verifying key from an RSA public key using provided label pub fn new_with_label>(key: RsaPrivateKey, label: S) -> Self { Self { inner: key, label: Some(label.as_ref().to_string()), phantom: Default::default(), mg_phantom: Default::default(), } } } impl Decryptor for DecryptingKey where D: Digest, MGD: Digest + FixedOutputReset, { fn decrypt(&self, ciphertext: &[u8]) -> Result> { decrypt_digest::( None, &self.inner, ciphertext, self.label.as_ref().cloned(), ) } } impl RandomizedDecryptor for DecryptingKey where D: Digest, MGD: Digest + FixedOutputReset, { fn decrypt_with_rng( &self, rng: &mut R, ciphertext: &[u8], ) -> Result> { decrypt_digest::<_, D, MGD>( Some(rng), &self.inner, ciphertext, self.label.as_ref().cloned(), ) } } impl ZeroizeOnDrop for DecryptingKey where D: Digest, MGD: Digest + FixedOutputReset, { } rsa-0.9.7/src/oaep/encrypting_key.rs000064400000000000000000000032751046102023000155500ustar 00000000000000use super::encrypt_digest; use crate::{traits::RandomizedEncryptor, Result, RsaPublicKey}; use alloc::{ string::{String, ToString}, vec::Vec, }; use core::marker::PhantomData; use digest::{Digest, FixedOutputReset}; use rand_core::CryptoRngCore; /// Encryption key for PKCS#1 v1.5 encryption as described in [RFC8017 § 7.1]. /// /// [RFC8017 § 7.1]: https://datatracker.ietf.org/doc/html/rfc8017#section-7.1 #[derive(Debug, Clone)] pub struct EncryptingKey where D: Digest, MGD: Digest + FixedOutputReset, { inner: RsaPublicKey, label: Option, phantom: PhantomData, mg_phantom: PhantomData, } impl EncryptingKey where D: Digest, MGD: Digest + FixedOutputReset, { /// Create a new verifying key from an RSA public key. pub fn new(key: RsaPublicKey) -> Self { Self { inner: key, label: None, phantom: Default::default(), mg_phantom: Default::default(), } } /// Create a new verifying key from an RSA public key using provided label pub fn new_with_label>(key: RsaPublicKey, label: S) -> Self { Self { inner: key, label: Some(label.as_ref().to_string()), phantom: Default::default(), mg_phantom: Default::default(), } } } impl RandomizedEncryptor for EncryptingKey where D: Digest, MGD: Digest + FixedOutputReset, { fn encrypt_with_rng( &self, rng: &mut R, msg: &[u8], ) -> Result> { encrypt_digest::<_, D, MGD>(rng, &self.inner, msg, self.label.as_ref().cloned()) } } rsa-0.9.7/src/oaep.rs000064400000000000000000000571541046102023000125230ustar 00000000000000//! Encryption and Decryption using [OAEP padding](https://datatracker.ietf.org/doc/html/rfc8017#section-7.1). //! //! # Usage //! //! See [code example in the toplevel rustdoc](../index.html#oaep-encryption). mod decrypting_key; mod encrypting_key; pub use self::{decrypting_key::DecryptingKey, encrypting_key::EncryptingKey}; use alloc::boxed::Box; use alloc::string::{String, ToString}; use alloc::vec::Vec; use core::fmt; use digest::{Digest, DynDigest, FixedOutputReset}; use num_bigint::BigUint; use rand_core::CryptoRngCore; use zeroize::Zeroizing; use crate::algorithms::oaep::*; use crate::algorithms::pad::{uint_to_be_pad, uint_to_zeroizing_be_pad}; use crate::algorithms::rsa::{rsa_decrypt_and_check, rsa_encrypt}; use crate::errors::{Error, Result}; use crate::key::{self, RsaPrivateKey, RsaPublicKey}; use crate::traits::{PaddingScheme, PublicKeyParts}; /// Encryption and Decryption using [OAEP padding](https://datatracker.ietf.org/doc/html/rfc8017#section-7.1). /// /// - `digest` is used to hash the label. The maximum possible plaintext length is `m = k - 2 * h_len - 2`, /// where `k` is the size of the RSA modulus. /// - `mgf_digest` specifies the hash function that is used in the [MGF1](https://datatracker.ietf.org/doc/html/rfc8017#appendix-B.2). /// - `label` is optional data that can be associated with the message. /// /// The two hash functions can, but don't need to be the same. /// /// A prominent example is the [`AndroidKeyStore`](https://developer.android.com/guide/topics/security/cryptography#oaep-mgf1-digest). /// It uses SHA-1 for `mgf_digest` and a user-chosen SHA flavour for `digest`. pub struct Oaep { /// Digest type to use. pub digest: Box, /// Digest to use for Mask Generation Function (MGF). pub mgf_digest: Box, /// Optional label. pub label: Option, } impl Oaep { /// Create a new OAEP `PaddingScheme`, using `T` as the hash function for both the default (empty) label and for MGF1. /// /// # Example /// ``` /// use sha1::Sha1; /// use sha2::Sha256; /// use rsa::{BigUint, RsaPublicKey, Oaep, }; /// use base64ct::{Base64, Encoding}; /// /// let n = Base64::decode_vec("ALHgDoZmBQIx+jTmgeeHW6KsPOrj11f6CvWsiRleJlQpW77AwSZhd21ZDmlTKfaIHBSUxRUsuYNh7E2SHx8rkFVCQA2/gXkZ5GK2IUbzSTio9qXA25MWHvVxjMfKSL8ZAxZyKbrG94FLLszFAFOaiLLY8ECs7g+dXOriYtBwLUJK+lppbd+El+8ZA/zH0bk7vbqph5pIoiWggxwdq3mEz4LnrUln7r6dagSQzYErKewY8GADVpXcq5mfHC1xF2DFBub7bFjMVM5fHq7RK+pG5xjNDiYITbhLYrbVv3X0z75OvN0dY49ITWjM7xyvMWJXVJS7sJlgmCCL6RwWgP8PhcE=").unwrap(); /// let e = Base64::decode_vec("AQAB").unwrap(); /// /// let mut rng = rand::thread_rng(); /// let key = RsaPublicKey::new(BigUint::from_bytes_be(&n), BigUint::from_bytes_be(&e)).unwrap(); /// let padding = Oaep::new::(); /// let encrypted_data = key.encrypt(&mut rng, padding, b"secret").unwrap(); /// ``` pub fn new() -> Self { Self { digest: Box::new(T::new()), mgf_digest: Box::new(T::new()), label: None, } } /// Create a new OAEP `PaddingScheme` with an associated `label`, using `T` as the hash function for both the label and for MGF1. pub fn new_with_label>( label: S, ) -> Self { Self { digest: Box::new(T::new()), mgf_digest: Box::new(T::new()), label: Some(label.as_ref().to_string()), } } /// Create a new OAEP `PaddingScheme`, using `T` as the hash function for the default (empty) label, and `U` as the hash function for MGF1. /// If a label is needed use `PaddingScheme::new_oaep_with_label` or `PaddingScheme::new_oaep_with_mgf_hash_with_label`. /// /// # Example /// ``` /// use sha1::Sha1; /// use sha2::Sha256; /// use rsa::{BigUint, RsaPublicKey, Oaep, }; /// use base64ct::{Base64, Encoding}; /// /// let n = Base64::decode_vec("ALHgDoZmBQIx+jTmgeeHW6KsPOrj11f6CvWsiRleJlQpW77AwSZhd21ZDmlTKfaIHBSUxRUsuYNh7E2SHx8rkFVCQA2/gXkZ5GK2IUbzSTio9qXA25MWHvVxjMfKSL8ZAxZyKbrG94FLLszFAFOaiLLY8ECs7g+dXOriYtBwLUJK+lppbd+El+8ZA/zH0bk7vbqph5pIoiWggxwdq3mEz4LnrUln7r6dagSQzYErKewY8GADVpXcq5mfHC1xF2DFBub7bFjMVM5fHq7RK+pG5xjNDiYITbhLYrbVv3X0z75OvN0dY49ITWjM7xyvMWJXVJS7sJlgmCCL6RwWgP8PhcE=").unwrap(); /// let e = Base64::decode_vec("AQAB").unwrap(); /// /// let mut rng = rand::thread_rng(); /// let key = RsaPublicKey::new(BigUint::from_bytes_be(&n), BigUint::from_bytes_be(&e)).unwrap(); /// let padding = Oaep::new_with_mgf_hash::(); /// let encrypted_data = key.encrypt(&mut rng, padding, b"secret").unwrap(); /// ``` pub fn new_with_mgf_hash< T: 'static + Digest + DynDigest + Send + Sync, U: 'static + Digest + DynDigest + Send + Sync, >() -> Self { Self { digest: Box::new(T::new()), mgf_digest: Box::new(U::new()), label: None, } } /// Create a new OAEP `PaddingScheme` with an associated `label`, using `T` as the hash function for the label, and `U` as the hash function for MGF1. pub fn new_with_mgf_hash_and_label< T: 'static + Digest + DynDigest + Send + Sync, U: 'static + Digest + DynDigest + Send + Sync, S: AsRef, >( label: S, ) -> Self { Self { digest: Box::new(T::new()), mgf_digest: Box::new(U::new()), label: Some(label.as_ref().to_string()), } } } impl PaddingScheme for Oaep { fn decrypt( mut self, rng: Option<&mut Rng>, priv_key: &RsaPrivateKey, ciphertext: &[u8], ) -> Result> { decrypt( rng, priv_key, ciphertext, &mut *self.digest, &mut *self.mgf_digest, self.label, ) } fn encrypt( mut self, rng: &mut Rng, pub_key: &RsaPublicKey, msg: &[u8], ) -> Result> { encrypt( rng, pub_key, msg, &mut *self.digest, &mut *self.mgf_digest, self.label, ) } } impl fmt::Debug for Oaep { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("OAEP") .field("digest", &"...") .field("mgf_digest", &"...") .field("label", &self.label) .finish() } } /// Encrypts the given message with RSA and the padding scheme from /// [PKCS#1 OAEP]. /// /// The message must be no longer than the length of the public modulus minus /// `2 + (2 * hash.size())`. /// /// [PKCS#1 OAEP]: https://datatracker.ietf.org/doc/html/rfc8017#section-7.1 #[inline] fn encrypt( rng: &mut R, pub_key: &RsaPublicKey, msg: &[u8], digest: &mut dyn DynDigest, mgf_digest: &mut dyn DynDigest, label: Option, ) -> Result> { key::check_public(pub_key)?; let em = oaep_encrypt(rng, msg, digest, mgf_digest, label, pub_key.size())?; let int = Zeroizing::new(BigUint::from_bytes_be(&em)); uint_to_be_pad(rsa_encrypt(pub_key, &int)?, pub_key.size()) } /// Encrypts the given message with RSA and the padding scheme from /// [PKCS#1 OAEP]. /// /// The message must be no longer than the length of the public modulus minus /// `2 + (2 * hash.size())`. /// /// [PKCS#1 OAEP]: https://datatracker.ietf.org/doc/html/rfc8017#section-7.1 fn encrypt_digest( rng: &mut R, pub_key: &RsaPublicKey, msg: &[u8], label: Option, ) -> Result> { key::check_public(pub_key)?; let em = oaep_encrypt_digest::<_, D, MGD>(rng, msg, label, pub_key.size())?; let int = Zeroizing::new(BigUint::from_bytes_be(&em)); uint_to_be_pad(rsa_encrypt(pub_key, &int)?, pub_key.size()) } /// Decrypts a plaintext using RSA and the padding scheme from [PKCS#1 OAEP]. /// /// If an `rng` is passed, it uses RSA blinding to avoid timing side-channel attacks. /// /// Note that whether this function returns an error or not discloses secret /// information. If an attacker can cause this function to run repeatedly and /// learn whether each instance returned an error then they can decrypt and /// forge signatures as if they had the private key. /// /// See `decrypt_session_key` for a way of solving this problem. /// /// [PKCS#1 OAEP]: https://datatracker.ietf.org/doc/html/rfc8017#section-7.1 #[inline] fn decrypt( rng: Option<&mut R>, priv_key: &RsaPrivateKey, ciphertext: &[u8], digest: &mut dyn DynDigest, mgf_digest: &mut dyn DynDigest, label: Option, ) -> Result> { key::check_public(priv_key)?; if ciphertext.len() != priv_key.size() { return Err(Error::Decryption); } let em = rsa_decrypt_and_check(priv_key, rng, &BigUint::from_bytes_be(ciphertext))?; let mut em = uint_to_zeroizing_be_pad(em, priv_key.size())?; oaep_decrypt(&mut em, digest, mgf_digest, label, priv_key.size()) } /// Decrypts a plaintext using RSA and the padding scheme from [PKCS#1 OAEP]. /// /// If an `rng` is passed, it uses RSA blinding to avoid timing side-channel attacks. /// /// Note that whether this function returns an error or not discloses secret /// information. If an attacker can cause this function to run repeatedly and /// learn whether each instance returned an error then they can decrypt and /// forge signatures as if they had the private key. /// /// See `decrypt_session_key` for a way of solving this problem. /// /// [PKCS#1 OAEP]: https://datatracker.ietf.org/doc/html/rfc8017#section-7.1 #[inline] fn decrypt_digest( rng: Option<&mut R>, priv_key: &RsaPrivateKey, ciphertext: &[u8], label: Option, ) -> Result> { key::check_public(priv_key)?; if ciphertext.len() != priv_key.size() { return Err(Error::Decryption); } let em = rsa_decrypt_and_check(priv_key, rng, &BigUint::from_bytes_be(ciphertext))?; let mut em = uint_to_zeroizing_be_pad(em, priv_key.size())?; oaep_decrypt_digest::(&mut em, label, priv_key.size()) } #[cfg(test)] mod tests { use crate::key::{RsaPrivateKey, RsaPublicKey}; use crate::oaep::{DecryptingKey, EncryptingKey, Oaep}; use crate::traits::PublicKeyParts; use crate::traits::{Decryptor, RandomizedDecryptor, RandomizedEncryptor}; use alloc::string::String; use digest::{Digest, DynDigest, FixedOutputReset}; use num_bigint::BigUint; use num_traits::FromPrimitive; use rand_chacha::{ rand_core::{RngCore, SeedableRng}, ChaCha8Rng, }; use sha1::Sha1; use sha2::{Sha224, Sha256, Sha384, Sha512}; use sha3::{Sha3_256, Sha3_384, Sha3_512}; fn get_private_key() -> RsaPrivateKey { // -----BEGIN RSA PRIVATE KEY----- // MIIEpAIBAAKCAQEA05e4TZikwmE47RtpWoEG6tkdVTvwYEG2LT/cUKBB4iK49FKW // icG4LF5xVU9d1p+i9LYVjPDb61eBGg/DJ+HyjnT+dNO8Fmweq9wbi1e5NMqL5bAL // TymXW8yZrK9BW1m7KKZ4K7QaLDwpdrPBjbre9i8AxrsiZkAJUJbAzGDSL+fvmH11 // xqgbENlr8pICivEQ3HzBu8Q9Iq2rN5oM1dgHjMeA/1zWIJ3qNMkiz3hPdxfkKNdb // WuyP8w5fAUFRB2bi4KuNRzyE6HELK5gifD2wlTN600UvGeK5v7zN2BSKv2d2+lUn // debnWVbkUimuWpxGlJurHmIvDkj1ZSSoTtNIOwIDAQABAoIBAQDE5wxokWLJTGYI // KBkbUrTYOSEV30hqmtvoMeRY1zlYMg3Bt1VFbpNwHpcC12+wuS+Q4B0f4kgVMoH+ // eaqXY6kvrmnY1+zRRN4p+hNb0U+Vc+NJ5FAx47dpgvWDADgmxVLomjl8Gga9IWNI // hjDZLowrtkPXq+9wDaldaFyUFImkb1S1MW9itdLDp/G70TTLNzU6RGg/3J2V02RY // 3iL2xEBX/nSgpDbEMI9z9NpC81xHrBanE41IOvyR5B3DoRJzguDA9RGbAiG0/GOd // a5w4F3pt6bUm69iMONeYLAf5ig79h31Qiq4nW5RpFcAuLhEG0XXXTsZ3f16A0SwF // PZx74eNBAoGBAPgnu/OkGHfHzFmuv0LtSynDLe/LjtloY9WwkKBaiTDdYkohydz5 // g4Vo/foN9luEYqXyrJE9bFb5dVMr2OePsHvUBcqZpIS89Z8Bm73cs5M/K85wYwC0 // 97EQEgxd+QGBWQZ8NdowYaVshjWlK1QnOzEnG0MR8Hld9gIeY1XhpC5hAoGBANpI // F84Aid028q3mo/9BDHPsNL8bT2vaOEMb/t4RzvH39u+nDl+AY6Ox9uFylv+xX+76 // CRKgMluNH9ZaVZ5xe1uWHsNFBy4OxSA9A0QdKa9NZAVKBFB0EM8dp457YRnZCexm // 5q1iW/mVsnmks8W+fYlc18W5xMSX/ecwkW/NtOQbAoGAHabpz4AhKFbodSLrWbzv // CUt4NroVFKdjnoodjfujfwJFF2SYMV5jN9LG3lVCxca43ulzc1tqka33Nfv8TBcg // WHuKQZ5ASVgm5VwU1wgDMSoQOve07MWy/yZTccTc1zA0ihDXgn3bfR/NnaVh2wlh // CkuI92eyW1494hztc7qlmqECgYEA1zenyOQ9ChDIW/ABGIahaZamNxsNRrDFMl3j // AD+cxHSRU59qC32CQH8ShRy/huHzTaPX2DZ9EEln76fnrS4Ey7uLH0rrFl1XvT6K // /timJgLvMEvXTx/xBtUdRN2fUqXtI9odbSyCtOYFL+zVl44HJq2UzY4pVRDrNcxs // SUkQJqsCgYBSaNfPBzR5rrstLtTdZrjImRW1LRQeDEky9WsMDtCTYUGJTsTSfVO8 // hkU82MpbRVBFIYx+GWIJwcZRcC7OCQoV48vMJllxMAAjqG/p00rVJ+nvA7et/nNu // BoB0er/UmDm4Ly/97EO9A0PKMOE5YbMq9s3t3RlWcsdrU7dvw+p2+A== // -----END RSA PRIVATE KEY----- RsaPrivateKey::from_components( BigUint::parse_bytes(b"00d397b84d98a4c26138ed1b695a8106ead91d553bf06041b62d3fdc50a041e222b8f4529689c1b82c5e71554f5dd69fa2f4b6158cf0dbeb57811a0fc327e1f28e74fe74d3bc166c1eabdc1b8b57b934ca8be5b00b4f29975bcc99acaf415b59bb28a6782bb41a2c3c2976b3c18dbadef62f00c6bb226640095096c0cc60d22fe7ef987d75c6a81b10d96bf292028af110dc7cc1bbc43d22adab379a0cd5d8078cc780ff5cd6209dea34c922cf784f7717e428d75b5aec8ff30e5f0141510766e2e0ab8d473c84e8710b2b98227c3db095337ad3452f19e2b9bfbccdd8148abf6776fa552775e6e75956e45229ae5a9c46949bab1e622f0e48f56524a84ed3483b", 16).unwrap(), BigUint::from_u64(65537).unwrap(), BigUint::parse_bytes(b"00c4e70c689162c94c660828191b52b4d8392115df486a9adbe831e458d73958320dc1b755456e93701e9702d76fb0b92f90e01d1fe248153281fe79aa9763a92fae69d8d7ecd144de29fa135bd14f9573e349e45031e3b76982f583003826c552e89a397c1a06bd2163488630d92e8c2bb643d7abef700da95d685c941489a46f54b5316f62b5d2c3a7f1bbd134cb37353a44683fdc9d95d36458de22f6c44057fe74a0a436c4308f73f4da42f35c47ac16a7138d483afc91e41dc3a1127382e0c0f5119b0221b4fc639d6b9c38177a6de9b526ebd88c38d7982c07f98a0efd877d508aae275b946915c02e2e1106d175d74ec6777f5e80d12c053d9c7be1e341", 16).unwrap(), vec![ BigUint::parse_bytes(b"00f827bbf3a41877c7cc59aebf42ed4b29c32defcb8ed96863d5b090a05a8930dd624a21c9dcf9838568fdfa0df65b8462a5f2ac913d6c56f975532bd8e78fb07bd405ca99a484bcf59f019bbddcb3933f2bce706300b4f7b110120c5df9018159067c35da3061a56c8635a52b54273b31271b4311f0795df6021e6355e1a42e61",16).unwrap(), BigUint::parse_bytes(b"00da4817ce0089dd36f2ade6a3ff410c73ec34bf1b4f6bda38431bfede11cef1f7f6efa70e5f8063a3b1f6e17296ffb15feefa0912a0325b8d1fd65a559e717b5b961ec345072e0ec5203d03441d29af4d64054a04507410cf1da78e7b6119d909ec66e6ad625bf995b279a4b3c5be7d895cd7c5b9c4c497fde730916fcdb4e41b", 16).unwrap() ], ).unwrap() } #[test] fn test_encrypt_decrypt_oaep() { let priv_key = get_private_key(); do_test_encrypt_decrypt_oaep::(&priv_key); do_test_encrypt_decrypt_oaep::(&priv_key); do_test_encrypt_decrypt_oaep::(&priv_key); do_test_encrypt_decrypt_oaep::(&priv_key); do_test_encrypt_decrypt_oaep::(&priv_key); do_test_encrypt_decrypt_oaep::(&priv_key); do_test_encrypt_decrypt_oaep::(&priv_key); do_test_encrypt_decrypt_oaep::(&priv_key); do_test_oaep_with_different_hashes::(&priv_key); do_test_oaep_with_different_hashes::(&priv_key); do_test_oaep_with_different_hashes::(&priv_key); do_test_oaep_with_different_hashes::(&priv_key); do_test_oaep_with_different_hashes::(&priv_key); do_test_oaep_with_different_hashes::(&priv_key); do_test_oaep_with_different_hashes::(&priv_key); do_test_oaep_with_different_hashes::(&priv_key); } fn get_label(rng: &mut ChaCha8Rng) -> Option { const GEN_ASCII_STR_CHARSET: &[u8; 64] = b"ABCDEFGHIJKLMNOPQRSTUVWXYZ\ abcdefghijklmnopqrstuvwxyz\ 0123456789=+"; let mut buf = [0u8; 32]; rng.fill_bytes(&mut buf); if buf[0] < (1 << 7) { for v in buf.iter_mut() { *v = GEN_ASCII_STR_CHARSET[(*v >> 2) as usize]; } Some(core::str::from_utf8(&buf).unwrap().to_string()) } else { None } } fn do_test_encrypt_decrypt_oaep( prk: &RsaPrivateKey, ) { let mut rng = ChaCha8Rng::from_seed([42; 32]); let k = prk.size(); for i in 1..8 { let mut input = vec![0u8; i * 8]; rng.fill_bytes(&mut input); if input.len() > k - 11 { input = input[0..k - 11].to_vec(); } let label = get_label(&mut rng); let pub_key: RsaPublicKey = prk.into(); let ciphertext = if let Some(ref label) = label { let padding = Oaep::new_with_label::(label); pub_key.encrypt(&mut rng, padding, &input).unwrap() } else { let padding = Oaep::new::(); pub_key.encrypt(&mut rng, padding, &input).unwrap() }; assert_ne!(input, ciphertext); let blind: bool = rng.next_u32() < (1 << 31); let padding = if let Some(ref label) = label { Oaep::new_with_label::(label) } else { Oaep::new::() }; let plaintext = if blind { prk.decrypt(padding, &ciphertext).unwrap() } else { prk.decrypt_blinded(&mut rng, padding, &ciphertext).unwrap() }; assert_eq!(input, plaintext); } } fn do_test_oaep_with_different_hashes< D: 'static + Digest + DynDigest + Send + Sync, U: 'static + Digest + DynDigest + Send + Sync, >( prk: &RsaPrivateKey, ) { let mut rng = ChaCha8Rng::from_seed([42; 32]); let k = prk.size(); for i in 1..8 { let mut input = vec![0u8; i * 8]; rng.fill_bytes(&mut input); if input.len() > k - 11 { input = input[0..k - 11].to_vec(); } let label = get_label(&mut rng); let pub_key: RsaPublicKey = prk.into(); let ciphertext = if let Some(ref label) = label { let padding = Oaep::new_with_mgf_hash_and_label::(label); pub_key.encrypt(&mut rng, padding, &input).unwrap() } else { let padding = Oaep::new_with_mgf_hash::(); pub_key.encrypt(&mut rng, padding, &input).unwrap() }; assert_ne!(input, ciphertext); let blind: bool = rng.next_u32() < (1 << 31); let padding = if let Some(ref label) = label { Oaep::new_with_mgf_hash_and_label::(label) } else { Oaep::new_with_mgf_hash::() }; let plaintext = if blind { prk.decrypt(padding, &ciphertext).unwrap() } else { prk.decrypt_blinded(&mut rng, padding, &ciphertext).unwrap() }; assert_eq!(input, plaintext); } } #[test] fn test_decrypt_oaep_invalid_hash() { let mut rng = ChaCha8Rng::from_seed([42; 32]); let priv_key = get_private_key(); let pub_key: RsaPublicKey = (&priv_key).into(); let ciphertext = pub_key .encrypt(&mut rng, Oaep::new::(), "a_plain_text".as_bytes()) .unwrap(); assert!( priv_key .decrypt_blinded( &mut rng, Oaep::new_with_label::("label"), &ciphertext, ) .is_err(), "decrypt should have failed on hash verification" ); } #[test] fn test_encrypt_decrypt_oaep_traits() { let priv_key = get_private_key(); do_test_encrypt_decrypt_oaep_traits::(&priv_key); do_test_encrypt_decrypt_oaep_traits::(&priv_key); do_test_encrypt_decrypt_oaep_traits::(&priv_key); do_test_encrypt_decrypt_oaep_traits::(&priv_key); do_test_encrypt_decrypt_oaep_traits::(&priv_key); do_test_encrypt_decrypt_oaep_traits::(&priv_key); do_test_encrypt_decrypt_oaep_traits::(&priv_key); do_test_encrypt_decrypt_oaep_traits::(&priv_key); do_test_oaep_with_different_hashes_traits::(&priv_key); do_test_oaep_with_different_hashes_traits::(&priv_key); do_test_oaep_with_different_hashes_traits::(&priv_key); do_test_oaep_with_different_hashes_traits::(&priv_key); do_test_oaep_with_different_hashes_traits::(&priv_key); do_test_oaep_with_different_hashes_traits::(&priv_key); do_test_oaep_with_different_hashes_traits::(&priv_key); do_test_oaep_with_different_hashes_traits::(&priv_key); } fn do_test_encrypt_decrypt_oaep_traits(prk: &RsaPrivateKey) { do_test_oaep_with_different_hashes_traits::(prk); } fn do_test_oaep_with_different_hashes_traits( prk: &RsaPrivateKey, ) { let mut rng = ChaCha8Rng::from_seed([42; 32]); let k = prk.size(); for i in 1..8 { let mut input = vec![0u8; i * 8]; rng.fill_bytes(&mut input); if input.len() > k - 11 { input = input[0..k - 11].to_vec(); } let label = get_label(&mut rng); let pub_key: RsaPublicKey = prk.into(); let ciphertext = if let Some(ref label) = label { let encrypting_key = EncryptingKey::::new_with_label(pub_key, label.clone()); encrypting_key.encrypt_with_rng(&mut rng, &input).unwrap() } else { let encrypting_key = EncryptingKey::::new(pub_key); encrypting_key.encrypt_with_rng(&mut rng, &input).unwrap() }; assert_ne!(input, ciphertext); let blind: bool = rng.next_u32() < (1 << 31); let decrypting_key = if let Some(ref label) = label { DecryptingKey::::new_with_label(prk.clone(), label.clone()) } else { DecryptingKey::::new(prk.clone()) }; let plaintext = if blind { decrypting_key.decrypt(&ciphertext).unwrap() } else { decrypting_key .decrypt_with_rng(&mut rng, &ciphertext) .unwrap() }; assert_eq!(input, plaintext); } } #[test] fn test_decrypt_oaep_invalid_hash_traits() { let mut rng = ChaCha8Rng::from_seed([42; 32]); let priv_key = get_private_key(); let pub_key: RsaPublicKey = (&priv_key).into(); let encrypting_key = EncryptingKey::::new(pub_key); let decrypting_key = DecryptingKey::::new_with_label(priv_key, "label"); let ciphertext = encrypting_key .encrypt_with_rng(&mut rng, "a_plain_text".as_bytes()) .unwrap(); assert!( decrypting_key .decrypt_with_rng(&mut rng, &ciphertext) .is_err(), "decrypt should have failed on hash verification" ); } } rsa-0.9.7/src/pkcs1v15/decrypting_key.rs000064400000000000000000000024611046102023000161630ustar 00000000000000use super::{decrypt, EncryptingKey}; use crate::{ dummy_rng::DummyRng, traits::{Decryptor, EncryptingKeypair, RandomizedDecryptor}, Result, RsaPrivateKey, }; use alloc::vec::Vec; use rand_core::CryptoRngCore; use zeroize::ZeroizeOnDrop; /// Decryption key for PKCS#1 v1.5 decryption as described in [RFC8017 § 7.2]. /// /// [RFC8017 § 7.2]: https://datatracker.ietf.org/doc/html/rfc8017#section-7.2 #[derive(Debug, Clone)] pub struct DecryptingKey { inner: RsaPrivateKey, } impl DecryptingKey { /// Create a new verifying key from an RSA public key. pub fn new(key: RsaPrivateKey) -> Self { Self { inner: key } } } impl Decryptor for DecryptingKey { fn decrypt(&self, ciphertext: &[u8]) -> Result> { decrypt::(None, &self.inner, ciphertext) } } impl RandomizedDecryptor for DecryptingKey { fn decrypt_with_rng( &self, rng: &mut R, ciphertext: &[u8], ) -> Result> { decrypt(Some(rng), &self.inner, ciphertext) } } impl EncryptingKeypair for DecryptingKey { type EncryptingKey = EncryptingKey; fn encrypting_key(&self) -> EncryptingKey { EncryptingKey { inner: self.inner.clone().into(), } } } impl ZeroizeOnDrop for DecryptingKey {} rsa-0.9.7/src/pkcs1v15/encrypting_key.rs000064400000000000000000000014121046102023000161700ustar 00000000000000use super::encrypt; use crate::{traits::RandomizedEncryptor, Result, RsaPublicKey}; use alloc::vec::Vec; use rand_core::CryptoRngCore; /// Encryption key for PKCS#1 v1.5 encryption as described in [RFC8017 § 7.2]. /// /// [RFC8017 § 7.2]: https://datatracker.ietf.org/doc/html/rfc8017#section-7.2 #[derive(Debug, Clone)] pub struct EncryptingKey { pub(super) inner: RsaPublicKey, } impl EncryptingKey { /// Create a new verifying key from an RSA public key. pub fn new(key: RsaPublicKey) -> Self { Self { inner: key } } } impl RandomizedEncryptor for EncryptingKey { fn encrypt_with_rng( &self, rng: &mut R, msg: &[u8], ) -> Result> { encrypt(rng, &self.inner, msg) } } rsa-0.9.7/src/pkcs1v15/signature.rs000064400000000000000000000043411046102023000151430ustar 00000000000000pub use ::signature::{ hazmat::{PrehashSigner, PrehashVerifier}, DigestSigner, DigestVerifier, Error, Keypair, RandomizedDigestSigner, RandomizedSigner, Result, SignatureEncoding, Signer, Verifier, }; use spki::{ der::{asn1::BitString, Result as DerResult}, SignatureBitStringEncoding, }; use crate::algorithms::pad::uint_to_be_pad; use alloc::{boxed::Box, string::ToString}; use core::fmt::{Debug, Display, Formatter, LowerHex, UpperHex}; use num_bigint::BigUint; /// `RSASSA-PKCS1-v1_5` signatures as described in [RFC8017 § 8.2]. /// /// [RFC8017 § 8.2]: https://datatracker.ietf.org/doc/html/rfc8017#section-8.2 #[derive(Clone, PartialEq, Eq)] pub struct Signature { pub(super) inner: BigUint, pub(super) len: usize, } impl SignatureEncoding for Signature { type Repr = Box<[u8]>; } impl SignatureBitStringEncoding for Signature { fn to_bitstring(&self) -> DerResult { BitString::new(0, self.to_vec()) } } impl TryFrom<&[u8]> for Signature { type Error = signature::Error; fn try_from(bytes: &[u8]) -> signature::Result { Ok(Self { inner: BigUint::from_bytes_be(bytes), len: bytes.len(), }) } } impl From for Box<[u8]> { fn from(signature: Signature) -> Box<[u8]> { uint_to_be_pad(signature.inner, signature.len) .expect("RSASSA-PKCS1-v1_5 length invariants should've been enforced") .into_boxed_slice() } } impl Debug for Signature { fn fmt(&self, fmt: &mut Formatter<'_>) -> core::result::Result<(), core::fmt::Error> { fmt.debug_tuple("Signature") .field(&self.to_string()) .finish() } } impl LowerHex for Signature { fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result { for byte in self.to_bytes().iter() { write!(f, "{:02x}", byte)?; } Ok(()) } } impl UpperHex for Signature { fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result { for byte in self.to_bytes().iter() { write!(f, "{:02X}", byte)?; } Ok(()) } } impl Display for Signature { fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result { write!(f, "{:X}", self) } } rsa-0.9.7/src/pkcs1v15/signing_key.rs000064400000000000000000000142711046102023000154530ustar 00000000000000use super::{oid, pkcs1v15_generate_prefix, sign, Signature, VerifyingKey}; use crate::{dummy_rng::DummyRng, Result, RsaPrivateKey}; use alloc::vec::Vec; use core::marker::PhantomData; use digest::Digest; use pkcs8::{ spki::{ der::AnyRef, AlgorithmIdentifierRef, AssociatedAlgorithmIdentifier, SignatureAlgorithmIdentifier, }, AssociatedOid, EncodePrivateKey, SecretDocument, }; use rand_core::CryptoRngCore; use signature::{ hazmat::PrehashSigner, DigestSigner, Keypair, RandomizedDigestSigner, RandomizedSigner, Signer, }; use zeroize::ZeroizeOnDrop; /// Signing key for `RSASSA-PKCS1-v1_5` signatures as described in [RFC8017 § 8.2]. /// /// [RFC8017 § 8.2]: https://datatracker.ietf.org/doc/html/rfc8017#section-8.2 #[derive(Debug, Clone)] pub struct SigningKey where D: Digest, { inner: RsaPrivateKey, prefix: Vec, phantom: PhantomData, } impl SigningKey where D: Digest + AssociatedOid, { /// Create a new signing key with a prefix for the digest `D`. pub fn new(key: RsaPrivateKey) -> Self { Self { inner: key, prefix: pkcs1v15_generate_prefix::(), phantom: Default::default(), } } /// Generate a new signing key with a prefix for the digest `D`. pub fn random(rng: &mut R, bit_size: usize) -> Result { Ok(Self { inner: RsaPrivateKey::new(rng, bit_size)?, prefix: pkcs1v15_generate_prefix::(), phantom: Default::default(), }) } /// Create a new signing key with a prefix for the digest `D`. #[deprecated(since = "0.9.0", note = "use SigningKey::new instead")] pub fn new_with_prefix(key: RsaPrivateKey) -> Self { Self::new(key) } /// Generate a new signing key with a prefix for the digest `D`. #[deprecated(since = "0.9.0", note = "use SigningKey::random instead")] pub fn random_with_prefix( rng: &mut R, bit_size: usize, ) -> Result { Self::random(rng, bit_size) } } impl SigningKey where D: Digest, { /// Create a new signing key from the give RSA private key with an empty prefix. /// /// ## Note: unprefixed signatures are uncommon /// /// In most cases you'll want to use [`SigningKey::new`]. pub fn new_unprefixed(key: RsaPrivateKey) -> Self { Self { inner: key, prefix: Vec::new(), phantom: Default::default(), } } /// Generate a new signing key with an empty prefix. pub fn random_unprefixed( rng: &mut R, bit_size: usize, ) -> Result { Ok(Self { inner: RsaPrivateKey::new(rng, bit_size)?, prefix: Vec::new(), phantom: Default::default(), }) } } // // `*Signer` trait impls // impl DigestSigner for SigningKey where D: Digest, { fn try_sign_digest(&self, digest: D) -> signature::Result { sign::(None, &self.inner, &self.prefix, &digest.finalize())? .as_slice() .try_into() } } impl PrehashSigner for SigningKey where D: Digest, { fn sign_prehash(&self, prehash: &[u8]) -> signature::Result { sign::(None, &self.inner, &self.prefix, prehash)? .as_slice() .try_into() } } impl RandomizedDigestSigner for SigningKey where D: Digest, { fn try_sign_digest_with_rng( &self, rng: &mut impl CryptoRngCore, digest: D, ) -> signature::Result { sign(Some(rng), &self.inner, &self.prefix, &digest.finalize())? .as_slice() .try_into() } } impl RandomizedSigner for SigningKey where D: Digest, { fn try_sign_with_rng( &self, rng: &mut impl CryptoRngCore, msg: &[u8], ) -> signature::Result { sign(Some(rng), &self.inner, &self.prefix, &D::digest(msg))? .as_slice() .try_into() } } impl Signer for SigningKey where D: Digest, { fn try_sign(&self, msg: &[u8]) -> signature::Result { sign::(None, &self.inner, &self.prefix, &D::digest(msg))? .as_slice() .try_into() } } // // Other trait impls // impl AsRef for SigningKey where D: Digest, { fn as_ref(&self) -> &RsaPrivateKey { &self.inner } } impl AssociatedAlgorithmIdentifier for SigningKey where D: Digest, { type Params = AnyRef<'static>; const ALGORITHM_IDENTIFIER: AlgorithmIdentifierRef<'static> = pkcs1::ALGORITHM_ID; } impl EncodePrivateKey for SigningKey where D: Digest, { fn to_pkcs8_der(&self) -> pkcs8::Result { self.inner.to_pkcs8_der() } } impl From for SigningKey where D: Digest, { fn from(key: RsaPrivateKey) -> Self { Self::new_unprefixed(key) } } impl From> for RsaPrivateKey where D: Digest, { fn from(key: SigningKey) -> Self { key.inner } } impl Keypair for SigningKey where D: Digest, { type VerifyingKey = VerifyingKey; fn verifying_key(&self) -> Self::VerifyingKey { VerifyingKey { inner: self.inner.to_public_key(), prefix: self.prefix.clone(), phantom: Default::default(), } } } impl SignatureAlgorithmIdentifier for SigningKey where D: Digest + oid::RsaSignatureAssociatedOid, { type Params = AnyRef<'static>; const SIGNATURE_ALGORITHM_IDENTIFIER: AlgorithmIdentifierRef<'static> = AlgorithmIdentifierRef { oid: D::OID, parameters: Some(AnyRef::NULL), }; } impl TryFrom> for SigningKey where D: Digest + AssociatedOid, { type Error = pkcs8::Error; fn try_from(private_key_info: pkcs8::PrivateKeyInfo<'_>) -> pkcs8::Result { RsaPrivateKey::try_from(private_key_info).map(Self::new) } } impl ZeroizeOnDrop for SigningKey where D: Digest {} rsa-0.9.7/src/pkcs1v15/verifying_key.rs000064400000000000000000000111611046102023000160120ustar 00000000000000use super::{oid, pkcs1v15_generate_prefix, verify, Signature}; use crate::RsaPublicKey; use alloc::vec::Vec; use core::marker::PhantomData; use digest::Digest; use pkcs8::{ spki::{ der::AnyRef, AlgorithmIdentifierRef, AssociatedAlgorithmIdentifier, SignatureAlgorithmIdentifier, }, AssociatedOid, Document, EncodePublicKey, }; use signature::{hazmat::PrehashVerifier, DigestVerifier, Verifier}; /// Verifying key for `RSASSA-PKCS1-v1_5` signatures as described in [RFC8017 § 8.2]. /// /// [RFC8017 § 8.2]: https://datatracker.ietf.org/doc/html/rfc8017#section-8.2 #[derive(Debug)] pub struct VerifyingKey where D: Digest, { pub(super) inner: RsaPublicKey, pub(super) prefix: Vec, pub(super) phantom: PhantomData, } impl VerifyingKey where D: Digest + AssociatedOid, { /// Create a new verifying key with a prefix for the digest `D`. pub fn new(key: RsaPublicKey) -> Self { Self { inner: key, prefix: pkcs1v15_generate_prefix::(), phantom: Default::default(), } } /// Create a new verifying key with a prefix for the digest `D`. #[deprecated(since = "0.9.0", note = "use VerifyingKey::new instead")] pub fn new_with_prefix(key: RsaPublicKey) -> Self { Self::new(key) } } impl VerifyingKey where D: Digest, { /// Create a new verifying key from an RSA public key with an empty prefix. /// /// ## Note: unprefixed signatures are uncommon /// /// In most cases you'll want to use [`VerifyingKey::new`] instead. pub fn new_unprefixed(key: RsaPublicKey) -> Self { Self { inner: key, prefix: Vec::new(), phantom: Default::default(), } } } // // `*Verifier` trait impls // impl DigestVerifier for VerifyingKey where D: Digest, { fn verify_digest(&self, digest: D, signature: &Signature) -> signature::Result<()> { verify( &self.inner, &self.prefix, &digest.finalize(), &signature.inner, signature.len, ) .map_err(|e| e.into()) } } impl PrehashVerifier for VerifyingKey where D: Digest, { fn verify_prehash(&self, prehash: &[u8], signature: &Signature) -> signature::Result<()> { verify( &self.inner, &self.prefix, prehash, &signature.inner, signature.len, ) .map_err(|e| e.into()) } } impl Verifier for VerifyingKey where D: Digest, { fn verify(&self, msg: &[u8], signature: &Signature) -> signature::Result<()> { verify( &self.inner, &self.prefix.clone(), &D::digest(msg), &signature.inner, signature.len, ) .map_err(|e| e.into()) } } // // Other trait impls // impl AsRef for VerifyingKey where D: Digest, { fn as_ref(&self) -> &RsaPublicKey { &self.inner } } impl AssociatedAlgorithmIdentifier for VerifyingKey where D: Digest, { type Params = AnyRef<'static>; const ALGORITHM_IDENTIFIER: AlgorithmIdentifierRef<'static> = pkcs1::ALGORITHM_ID; } // Implemented manually so we don't have to bind D with Clone impl Clone for VerifyingKey where D: Digest, { fn clone(&self) -> Self { Self { inner: self.inner.clone(), prefix: self.prefix.clone(), phantom: Default::default(), } } } impl EncodePublicKey for VerifyingKey where D: Digest, { fn to_public_key_der(&self) -> pkcs8::spki::Result { self.inner.to_public_key_der() } } impl From for VerifyingKey where D: Digest, { fn from(key: RsaPublicKey) -> Self { Self::new_unprefixed(key) } } impl From> for RsaPublicKey where D: Digest, { fn from(key: VerifyingKey) -> Self { key.inner } } impl SignatureAlgorithmIdentifier for VerifyingKey where D: Digest + oid::RsaSignatureAssociatedOid, { type Params = AnyRef<'static>; const SIGNATURE_ALGORITHM_IDENTIFIER: AlgorithmIdentifierRef<'static> = AlgorithmIdentifierRef { oid: D::OID, parameters: Some(AnyRef::NULL), }; } impl TryFrom> for VerifyingKey where D: Digest + AssociatedOid, { type Error = pkcs8::spki::Error; fn try_from(spki: pkcs8::SubjectPublicKeyInfoRef<'_>) -> pkcs8::spki::Result { RsaPublicKey::try_from(spki).map(Self::new) } } rsa-0.9.7/src/pkcs1v15.rs000064400000000000000000000563451046102023000131550ustar 00000000000000//! PKCS#1 v1.5 support as described in [RFC8017 § 8.2]. //! //! # Usage //! //! See [code example in the toplevel rustdoc](../index.html#pkcs1-v15-signatures). //! //! [RFC8017 § 8.2]: https://datatracker.ietf.org/doc/html/rfc8017#section-8.2 mod decrypting_key; mod encrypting_key; mod signature; mod signing_key; mod verifying_key; pub use self::{ decrypting_key::DecryptingKey, encrypting_key::EncryptingKey, signature::Signature, signing_key::SigningKey, verifying_key::VerifyingKey, }; use alloc::{boxed::Box, vec::Vec}; use core::fmt::Debug; use digest::Digest; use num_bigint::BigUint; use pkcs8::AssociatedOid; use rand_core::CryptoRngCore; use zeroize::Zeroizing; use crate::algorithms::pad::{uint_to_be_pad, uint_to_zeroizing_be_pad}; use crate::algorithms::pkcs1v15::*; use crate::algorithms::rsa::{rsa_decrypt_and_check, rsa_encrypt}; use crate::errors::{Error, Result}; use crate::key::{self, RsaPrivateKey, RsaPublicKey}; use crate::traits::{PaddingScheme, PublicKeyParts, SignatureScheme}; /// Encryption using PKCS#1 v1.5 padding. #[derive(Clone, Copy, Debug, Default, Eq, PartialEq)] pub struct Pkcs1v15Encrypt; impl PaddingScheme for Pkcs1v15Encrypt { fn decrypt( self, rng: Option<&mut Rng>, priv_key: &RsaPrivateKey, ciphertext: &[u8], ) -> Result> { decrypt(rng, priv_key, ciphertext) } fn encrypt( self, rng: &mut Rng, pub_key: &RsaPublicKey, msg: &[u8], ) -> Result> { encrypt(rng, pub_key, msg) } } /// `RSASSA-PKCS1-v1_5`: digital signatures using PKCS#1 v1.5 padding. #[derive(Clone, Debug, Eq, PartialEq)] pub struct Pkcs1v15Sign { /// Length of hash to use. pub hash_len: Option, /// Prefix. pub prefix: Box<[u8]>, } impl Pkcs1v15Sign { /// Create new PKCS#1 v1.5 padding for the given digest. /// /// The digest must have an [`AssociatedOid`]. Make sure to enable the `oid` /// feature of the relevant digest crate. pub fn new() -> Self where D: Digest + AssociatedOid, { Self { hash_len: Some(::output_size()), prefix: pkcs1v15_generate_prefix::().into_boxed_slice(), } } /// Create new PKCS#1 v1.5 padding for computing an unprefixed signature. /// /// This sets `hash_len` to `None` and uses an empty `prefix`. pub fn new_unprefixed() -> Self { Self { hash_len: None, prefix: Box::new([]), } } /// Create new PKCS#1 v1.5 padding for computing an unprefixed signature. /// /// This sets `hash_len` to `None` and uses an empty `prefix`. #[deprecated(since = "0.9.0", note = "use Pkcs1v15Sign::new_unprefixed instead")] pub fn new_raw() -> Self { Self::new_unprefixed() } } impl SignatureScheme for Pkcs1v15Sign { fn sign( self, rng: Option<&mut Rng>, priv_key: &RsaPrivateKey, hashed: &[u8], ) -> Result> { if let Some(hash_len) = self.hash_len { if hashed.len() != hash_len { return Err(Error::InputNotHashed); } } sign(rng, priv_key, &self.prefix, hashed) } fn verify(self, pub_key: &RsaPublicKey, hashed: &[u8], sig: &[u8]) -> Result<()> { if let Some(hash_len) = self.hash_len { if hashed.len() != hash_len { return Err(Error::InputNotHashed); } } verify( pub_key, self.prefix.as_ref(), hashed, &BigUint::from_bytes_be(sig), sig.len(), ) } } /// Encrypts the given message with RSA and the padding /// scheme from PKCS#1 v1.5. The message must be no longer than the /// length of the public modulus minus 11 bytes. #[inline] fn encrypt( rng: &mut R, pub_key: &RsaPublicKey, msg: &[u8], ) -> Result> { key::check_public(pub_key)?; let em = pkcs1v15_encrypt_pad(rng, msg, pub_key.size())?; let int = Zeroizing::new(BigUint::from_bytes_be(&em)); uint_to_be_pad(rsa_encrypt(pub_key, &int)?, pub_key.size()) } /// Decrypts a plaintext using RSA and the padding scheme from PKCS#1 v1.5. /// /// If an `rng` is passed, it uses RSA blinding to avoid timing side-channel attacks. /// /// Note that whether this function returns an error or not discloses secret /// information. If an attacker can cause this function to run repeatedly and /// learn whether each instance returned an error then they can decrypt and /// forge signatures as if they had the private key. See /// `decrypt_session_key` for a way of solving this problem. #[inline] fn decrypt( rng: Option<&mut R>, priv_key: &RsaPrivateKey, ciphertext: &[u8], ) -> Result> { key::check_public(priv_key)?; let em = rsa_decrypt_and_check(priv_key, rng, &BigUint::from_bytes_be(ciphertext))?; let em = uint_to_zeroizing_be_pad(em, priv_key.size())?; pkcs1v15_encrypt_unpad(em, priv_key.size()) } /// Calculates the signature of hashed using /// RSASSA-PKCS1-V1_5-SIGN from RSA PKCS#1 v1.5. Note that `hashed` must /// be the result of hashing the input message using the given hash /// function. If hash is `None`, hashed is signed directly. This isn't /// advisable except for interoperability. /// /// If `rng` is not `None` then RSA blinding will be used to avoid timing /// side-channel attacks. /// /// This function is deterministic. Thus, if the set of possible /// messages is small, an attacker may be able to build a map from /// messages to signatures and identify the signed messages. As ever, /// signatures provide authenticity, not confidentiality. #[inline] fn sign( rng: Option<&mut R>, priv_key: &RsaPrivateKey, prefix: &[u8], hashed: &[u8], ) -> Result> { let em = pkcs1v15_sign_pad(prefix, hashed, priv_key.size())?; uint_to_zeroizing_be_pad( rsa_decrypt_and_check(priv_key, rng, &BigUint::from_bytes_be(&em))?, priv_key.size(), ) } /// Verifies an RSA PKCS#1 v1.5 signature. #[inline] fn verify( pub_key: &RsaPublicKey, prefix: &[u8], hashed: &[u8], sig: &BigUint, sig_len: usize, ) -> Result<()> { if sig >= pub_key.n() || sig_len != pub_key.size() { return Err(Error::Verification); } let em = uint_to_be_pad(rsa_encrypt(pub_key, sig)?, pub_key.size())?; pkcs1v15_sign_unpad(prefix, hashed, &em, pub_key.size()) } mod oid { use const_oid::ObjectIdentifier; /// A trait which associates an RSA-specific OID with a type. pub trait RsaSignatureAssociatedOid { /// The OID associated with this type. const OID: ObjectIdentifier; } #[cfg(feature = "sha1")] impl RsaSignatureAssociatedOid for sha1::Sha1 { const OID: ObjectIdentifier = const_oid::ObjectIdentifier::new_unwrap("1.2.840.113549.1.1.5"); } #[cfg(feature = "sha2")] impl RsaSignatureAssociatedOid for sha2::Sha224 { const OID: ObjectIdentifier = const_oid::ObjectIdentifier::new_unwrap("1.2.840.113549.1.1.14"); } #[cfg(feature = "sha2")] impl RsaSignatureAssociatedOid for sha2::Sha256 { const OID: ObjectIdentifier = const_oid::ObjectIdentifier::new_unwrap("1.2.840.113549.1.1.11"); } #[cfg(feature = "sha2")] impl RsaSignatureAssociatedOid for sha2::Sha384 { const OID: ObjectIdentifier = const_oid::ObjectIdentifier::new_unwrap("1.2.840.113549.1.1.12"); } #[cfg(feature = "sha2")] impl RsaSignatureAssociatedOid for sha2::Sha512 { const OID: ObjectIdentifier = const_oid::ObjectIdentifier::new_unwrap("1.2.840.113549.1.1.13"); } } pub use oid::RsaSignatureAssociatedOid; #[cfg(test)] mod tests { use super::*; use ::signature::{ hazmat::{PrehashSigner, PrehashVerifier}, DigestSigner, DigestVerifier, Keypair, RandomizedDigestSigner, RandomizedSigner, SignatureEncoding, Signer, Verifier, }; use base64ct::{Base64, Encoding}; use hex_literal::hex; use num_bigint::BigUint; use num_traits::FromPrimitive; use num_traits::Num; use rand_chacha::{ rand_core::{RngCore, SeedableRng}, ChaCha8Rng, }; use sha1::{Digest, Sha1}; use sha2::Sha256; use sha3::Sha3_256; use crate::traits::{ Decryptor, EncryptingKeypair, PublicKeyParts, RandomizedDecryptor, RandomizedEncryptor, }; use crate::{RsaPrivateKey, RsaPublicKey}; fn get_private_key() -> RsaPrivateKey { // In order to generate new test vectors you'll need the PEM form of this key: // -----BEGIN RSA PRIVATE KEY----- // MIIBOgIBAAJBALKZD0nEffqM1ACuak0bijtqE2QrI/KLADv7l3kK3ppMyCuLKoF0 // fd7Ai2KW5ToIwzFofvJcS/STa6HA5gQenRUCAwEAAQJBAIq9amn00aS0h/CrjXqu // /ThglAXJmZhOMPVn4eiu7/ROixi9sex436MaVeMqSNf7Ex9a8fRNfWss7Sqd9eWu // RTUCIQDasvGASLqmjeffBNLTXV2A5g4t+kLVCpsEIZAycV5GswIhANEPLmax0ME/ // EO+ZJ79TJKN5yiGBRsv5yvx5UiHxajEXAiAhAol5N4EUyq6I9w1rYdhPMGpLfk7A // IU2snfRJ6Nq2CQIgFrPsWRCkV+gOYcajD17rEqmuLrdIRexpg8N1DOSXoJ8CIGlS // tAboUGBxTDq3ZroNism3DaMIbKPyYrAqhKov1h5V // -----END RSA PRIVATE KEY----- RsaPrivateKey::from_components( BigUint::from_str_radix("9353930466774385905609975137998169297361893554149986716853295022578535724979677252958524466350471210367835187480748268864277464700638583474144061408845077", 10).unwrap(), BigUint::from_u64(65537).unwrap(), BigUint::from_str_radix("7266398431328116344057699379749222532279343923819063639497049039389899328538543087657733766554155839834519529439851673014800261285757759040931985506583861", 10).unwrap(), vec![ BigUint::from_str_radix("98920366548084643601728869055592650835572950932266967461790948584315647051443",10).unwrap(), BigUint::from_str_radix("94560208308847015747498523884063394671606671904944666360068158221458669711639", 10).unwrap() ], ).unwrap() } #[test] fn test_decrypt_pkcs1v15() { let priv_key = get_private_key(); let tests = [[ "gIcUIoVkD6ATMBk/u/nlCZCCWRKdkfjCgFdo35VpRXLduiKXhNz1XupLLzTXAybEq15juc+EgY5o0DHv/nt3yg==", "x", ], [ "Y7TOCSqofGhkRb+jaVRLzK8xw2cSo1IVES19utzv6hwvx+M8kFsoWQm5DzBeJCZTCVDPkTpavUuEbgp8hnUGDw==", "testing.", ], [ "arReP9DJtEVyV2Dg3dDp4c/PSk1O6lxkoJ8HcFupoRorBZG+7+1fDAwT1olNddFnQMjmkb8vxwmNMoTAT/BFjQ==", "testing.\n", ], [ "WtaBXIoGC54+vH0NH0CHHE+dRDOsMc/6BrfFu2lEqcKL9+uDuWaf+Xj9mrbQCjjZcpQuX733zyok/jsnqe/Ftw==", "01234567890123456789012345678901234567890123456789012", ]]; for test in &tests { let out = priv_key .decrypt(Pkcs1v15Encrypt, &Base64::decode_vec(test[0]).unwrap()) .unwrap(); assert_eq!(out, test[1].as_bytes()); } } #[test] fn test_encrypt_decrypt_pkcs1v15() { let mut rng = ChaCha8Rng::from_seed([42; 32]); let priv_key = get_private_key(); let k = priv_key.size(); for i in 1..100 { let mut input = vec![0u8; i * 8]; rng.fill_bytes(&mut input); if input.len() > k - 11 { input = input[0..k - 11].to_vec(); } let pub_key: RsaPublicKey = priv_key.clone().into(); let ciphertext = encrypt(&mut rng, &pub_key, &input).unwrap(); assert_ne!(input, ciphertext); let blind: bool = rng.next_u32() < (1u32 << 31); let blinder = if blind { Some(&mut rng) } else { None }; let plaintext = decrypt(blinder, &priv_key, &ciphertext).unwrap(); assert_eq!(input, plaintext); } } #[test] fn test_decrypt_pkcs1v15_traits() { let priv_key = get_private_key(); let decrypting_key = DecryptingKey::new(priv_key); let tests = [[ "gIcUIoVkD6ATMBk/u/nlCZCCWRKdkfjCgFdo35VpRXLduiKXhNz1XupLLzTXAybEq15juc+EgY5o0DHv/nt3yg==", "x", ], [ "Y7TOCSqofGhkRb+jaVRLzK8xw2cSo1IVES19utzv6hwvx+M8kFsoWQm5DzBeJCZTCVDPkTpavUuEbgp8hnUGDw==", "testing.", ], [ "arReP9DJtEVyV2Dg3dDp4c/PSk1O6lxkoJ8HcFupoRorBZG+7+1fDAwT1olNddFnQMjmkb8vxwmNMoTAT/BFjQ==", "testing.\n", ], [ "WtaBXIoGC54+vH0NH0CHHE+dRDOsMc/6BrfFu2lEqcKL9+uDuWaf+Xj9mrbQCjjZcpQuX733zyok/jsnqe/Ftw==", "01234567890123456789012345678901234567890123456789012", ]]; for test in &tests { let out = decrypting_key .decrypt(&Base64::decode_vec(test[0]).unwrap()) .unwrap(); assert_eq!(out, test[1].as_bytes()); } } #[test] fn test_encrypt_decrypt_pkcs1v15_traits() { let mut rng = ChaCha8Rng::from_seed([42; 32]); let priv_key = get_private_key(); let k = priv_key.size(); let decrypting_key = DecryptingKey::new(priv_key); for i in 1..100 { let mut input = vec![0u8; i * 8]; rng.fill_bytes(&mut input); if input.len() > k - 11 { input = input[0..k - 11].to_vec(); } let encrypting_key = decrypting_key.encrypting_key(); let ciphertext = encrypting_key.encrypt_with_rng(&mut rng, &input).unwrap(); assert_ne!(input, ciphertext); let blind: bool = rng.next_u32() < (1u32 << 31); let plaintext = if blind { decrypting_key .decrypt_with_rng(&mut rng, &ciphertext) .unwrap() } else { decrypting_key.decrypt(&ciphertext).unwrap() }; assert_eq!(input, plaintext); } } #[test] fn test_sign_pkcs1v15() { let priv_key = get_private_key(); let tests = [( "Test.\n", hex!( "a4f3fa6ea93bcdd0c57be020c1193ecbfd6f200a3d95c409769b029578fa0e33" "6ad9a347600e40d3ae823b8c7e6bad88cc07c1d54c3a1523cbbb6d58efc362ae" ), )]; for (text, expected) in &tests { let digest = Sha1::digest(text.as_bytes()).to_vec(); let out = priv_key.sign(Pkcs1v15Sign::new::(), &digest).unwrap(); assert_ne!(out, digest); assert_eq!(out, expected); let mut rng = ChaCha8Rng::from_seed([42; 32]); let out2 = priv_key .sign_with_rng(&mut rng, Pkcs1v15Sign::new::(), &digest) .unwrap(); assert_eq!(out2, expected); } } #[test] fn test_sign_pkcs1v15_signer() { let priv_key = get_private_key(); let tests = [( "Test.\n", hex!( "a4f3fa6ea93bcdd0c57be020c1193ecbfd6f200a3d95c409769b029578fa0e33" "6ad9a347600e40d3ae823b8c7e6bad88cc07c1d54c3a1523cbbb6d58efc362ae" ), )]; let signing_key = SigningKey::::new(priv_key); for (text, expected) in &tests { let out = signing_key.sign(text.as_bytes()).to_bytes(); assert_ne!(out.as_ref(), text.as_bytes()); assert_ne!(out.as_ref(), &Sha1::digest(text.as_bytes()).to_vec()); assert_eq!(out.as_ref(), expected); let mut rng = ChaCha8Rng::from_seed([42; 32]); let out2 = signing_key .sign_with_rng(&mut rng, text.as_bytes()) .to_bytes(); assert_eq!(out2.as_ref(), expected); } } #[test] fn test_sign_pkcs1v15_signer_sha2_256() { let priv_key = get_private_key(); let tests = [( "Test.\n", hex!( "2ffae3f3e130287b3a1dcb320e46f52e8f3f7969b646932273a7e3a6f2a182ea" "02d42875a7ffa4a148aa311f9e4b562e4e13a2223fb15f4e5bf5f2b206d9451b" ), )]; let signing_key = SigningKey::::new(priv_key); for (text, expected) in &tests { let out = signing_key.sign(text.as_bytes()).to_bytes(); assert_ne!(out.as_ref(), text.as_bytes()); assert_eq!(out.as_ref(), expected); let mut rng = ChaCha8Rng::from_seed([42; 32]); let out2 = signing_key .sign_with_rng(&mut rng, text.as_bytes()) .to_bytes(); assert_eq!(out2.as_ref(), expected); } } #[test] fn test_sign_pkcs1v15_signer_sha3_256() { let priv_key = get_private_key(); let tests = [( "Test.\n", hex!( "55e9fba3354dfb51d2c8111794ea552c86afc2cab154652c03324df8c2c51ba7" "2ff7c14de59a6f9ba50d90c13a7537cc3011948369f1f0ec4a49d21eb7e723f9" ), )]; let signing_key = SigningKey::::new(priv_key); for (text, expected) in &tests { let out = signing_key.sign(text.as_bytes()).to_bytes(); assert_ne!(out.as_ref(), text.as_bytes()); assert_eq!(out.as_ref(), expected); let mut rng = ChaCha8Rng::from_seed([42; 32]); let out2 = signing_key .sign_with_rng(&mut rng, text.as_bytes()) .to_bytes(); assert_eq!(out2.as_ref(), expected); } } #[test] fn test_sign_pkcs1v15_digest_signer() { let priv_key = get_private_key(); let tests = [( "Test.\n", hex!( "a4f3fa6ea93bcdd0c57be020c1193ecbfd6f200a3d95c409769b029578fa0e33" "6ad9a347600e40d3ae823b8c7e6bad88cc07c1d54c3a1523cbbb6d58efc362ae" ), )]; let signing_key = SigningKey::new(priv_key); for (text, expected) in &tests { let mut digest = Sha1::new(); digest.update(text.as_bytes()); let out = signing_key.sign_digest(digest).to_bytes(); assert_ne!(out.as_ref(), text.as_bytes()); assert_ne!(out.as_ref(), &Sha1::digest(text.as_bytes()).to_vec()); assert_eq!(out.as_ref(), expected); let mut rng = ChaCha8Rng::from_seed([42; 32]); let mut digest = Sha1::new(); digest.update(text.as_bytes()); let out2 = signing_key .sign_digest_with_rng(&mut rng, digest) .to_bytes(); assert_eq!(out2.as_ref(), expected); } } #[test] fn test_verify_pkcs1v15() { let priv_key = get_private_key(); let tests = [ ( "Test.\n", hex!( "a4f3fa6ea93bcdd0c57be020c1193ecbfd6f200a3d95c409769b029578fa0e33" "6ad9a347600e40d3ae823b8c7e6bad88cc07c1d54c3a1523cbbb6d58efc362ae" ), true, ), ( "Test.\n", hex!( "a4f3fa6ea93bcdd0c57be020c1193ecbfd6f200a3d95c409769b029578fa0e33" "6ad9a347600e40d3ae823b8c7e6bad88cc07c1d54c3a1523cbbb6d58efc362af" ), false, ), ]; let pub_key: RsaPublicKey = priv_key.into(); for (text, sig, expected) in &tests { let digest = Sha1::digest(text.as_bytes()).to_vec(); let result = pub_key.verify(Pkcs1v15Sign::new::(), &digest, sig); match expected { true => result.expect("failed to verify"), false => { result.expect_err("expected verifying error"); } } } } #[test] fn test_verify_pkcs1v15_signer() { let priv_key = get_private_key(); let tests = [ ( "Test.\n", hex!( "a4f3fa6ea93bcdd0c57be020c1193ecbfd6f200a3d95c409769b029578fa0e33" "6ad9a347600e40d3ae823b8c7e6bad88cc07c1d54c3a1523cbbb6d58efc362ae" ), true, ), ( "Test.\n", hex!( "a4f3fa6ea93bcdd0c57be020c1193ecbfd6f200a3d95c409769b029578fa0e33" "6ad9a347600e40d3ae823b8c7e6bad88cc07c1d54c3a1523cbbb6d58efc362af" ), false, ), ]; let pub_key: RsaPublicKey = priv_key.into(); let verifying_key = VerifyingKey::::new(pub_key); for (text, sig, expected) in &tests { let result = verifying_key.verify( text.as_bytes(), &Signature::try_from(sig.as_slice()).unwrap(), ); match expected { true => result.expect("failed to verify"), false => { result.expect_err("expected verifying error"); } } } } #[test] fn test_verify_pkcs1v15_digest_signer() { let priv_key = get_private_key(); let tests = [ ( "Test.\n", hex!( "a4f3fa6ea93bcdd0c57be020c1193ecbfd6f200a3d95c409769b029578fa0e33" "6ad9a347600e40d3ae823b8c7e6bad88cc07c1d54c3a1523cbbb6d58efc362ae" ), true, ), ( "Test.\n", hex!( "a4f3fa6ea93bcdd0c57be020c1193ecbfd6f200a3d95c409769b029578fa0e33" "6ad9a347600e40d3ae823b8c7e6bad88cc07c1d54c3a1523cbbb6d58efc362af" ), false, ), ]; let pub_key: RsaPublicKey = priv_key.into(); let verifying_key = VerifyingKey::new(pub_key); for (text, sig, expected) in &tests { let mut digest = Sha1::new(); digest.update(text.as_bytes()); let result = verifying_key.verify_digest(digest, &Signature::try_from(sig.as_slice()).unwrap()); match expected { true => result.expect("failed to verify"), false => { result.expect_err("expected verifying error"); } } } } #[test] fn test_unpadded_signature() { let msg = b"Thu Dec 19 18:06:16 EST 2013\n"; let expected_sig = Base64::decode_vec("pX4DR8azytjdQ1rtUiC040FjkepuQut5q2ZFX1pTjBrOVKNjgsCDyiJDGZTCNoh9qpXYbhl7iEym30BWWwuiZg==").unwrap(); let priv_key = get_private_key(); let sig = priv_key.sign(Pkcs1v15Sign::new_unprefixed(), msg).unwrap(); assert_eq!(expected_sig, sig); let pub_key: RsaPublicKey = priv_key.into(); pub_key .verify(Pkcs1v15Sign::new_unprefixed(), msg, &sig) .expect("failed to verify"); } #[test] fn test_unpadded_signature_hazmat() { let msg = b"Thu Dec 19 18:06:16 EST 2013\n"; let expected_sig = Base64::decode_vec("pX4DR8azytjdQ1rtUiC040FjkepuQut5q2ZFX1pTjBrOVKNjgsCDyiJDGZTCNoh9qpXYbhl7iEym30BWWwuiZg==").unwrap(); let priv_key = get_private_key(); let signing_key = SigningKey::::new_unprefixed(priv_key); let sig = signing_key .sign_prehash(msg) .expect("Failure during sign") .to_bytes(); assert_eq!(sig.as_ref(), expected_sig); let verifying_key = signing_key.verifying_key(); verifying_key .verify_prehash(msg, &Signature::try_from(expected_sig.as_slice()).unwrap()) .expect("failed to verify"); } } rsa-0.9.7/src/pss/blinded_signing_key.rs000064400000000000000000000121171046102023000163610ustar 00000000000000use super::{get_pss_signature_algo_id, sign_digest, Signature, VerifyingKey}; use crate::{Result, RsaPrivateKey}; use const_oid::AssociatedOid; use core::marker::PhantomData; use digest::{Digest, FixedOutputReset}; use pkcs8::{ spki::{ der::AnyRef, AlgorithmIdentifierOwned, AlgorithmIdentifierRef, AssociatedAlgorithmIdentifier, DynSignatureAlgorithmIdentifier, }, EncodePrivateKey, SecretDocument, }; use rand_core::CryptoRngCore; use signature::{ hazmat::RandomizedPrehashSigner, Keypair, RandomizedDigestSigner, RandomizedSigner, }; use zeroize::ZeroizeOnDrop; /// Signing key for producing "blinded" RSASSA-PSS signatures as described in /// [draft-irtf-cfrg-rsa-blind-signatures](https://datatracker.ietf.org/doc/draft-irtf-cfrg-rsa-blind-signatures/). #[derive(Debug, Clone)] pub struct BlindedSigningKey where D: Digest, { inner: RsaPrivateKey, salt_len: usize, phantom: PhantomData, } impl BlindedSigningKey where D: Digest, { /// Create a new RSASSA-PSS signing key which produces "blinded" /// signatures. /// Digest output size is used as a salt length. pub fn new(key: RsaPrivateKey) -> Self { Self::new_with_salt_len(key, ::output_size()) } /// Create a new RSASSA-PSS signing key which produces "blinded" /// signatures with a salt of the given length. pub fn new_with_salt_len(key: RsaPrivateKey, salt_len: usize) -> Self { Self { inner: key, salt_len, phantom: Default::default(), } } /// Create a new random RSASSA-PSS signing key which produces "blinded" /// signatures. /// Digest output size is used as a salt length. pub fn random(rng: &mut R, bit_size: usize) -> Result { Self::random_with_salt_len(rng, bit_size, ::output_size()) } /// Create a new random RSASSA-PSS signing key which produces "blinded" /// signatures with a salt of the given length. pub fn random_with_salt_len( rng: &mut R, bit_size: usize, salt_len: usize, ) -> Result { Ok(Self { inner: RsaPrivateKey::new(rng, bit_size)?, salt_len, phantom: Default::default(), }) } /// Return specified salt length for this key pub fn salt_len(&self) -> usize { self.salt_len } } // // `*Signer` trait impls // impl RandomizedSigner for BlindedSigningKey where D: Digest + FixedOutputReset, { fn try_sign_with_rng( &self, rng: &mut impl CryptoRngCore, msg: &[u8], ) -> signature::Result { sign_digest::<_, D>(rng, true, &self.inner, &D::digest(msg), self.salt_len)? .as_slice() .try_into() } } impl RandomizedDigestSigner for BlindedSigningKey where D: Digest + FixedOutputReset, { fn try_sign_digest_with_rng( &self, rng: &mut impl CryptoRngCore, digest: D, ) -> signature::Result { sign_digest::<_, D>(rng, true, &self.inner, &digest.finalize(), self.salt_len)? .as_slice() .try_into() } } impl RandomizedPrehashSigner for BlindedSigningKey where D: Digest + FixedOutputReset, { fn sign_prehash_with_rng( &self, rng: &mut impl CryptoRngCore, prehash: &[u8], ) -> signature::Result { sign_digest::<_, D>(rng, true, &self.inner, prehash, self.salt_len)? .as_slice() .try_into() } } // // Other trait impls // impl AsRef for BlindedSigningKey where D: Digest, { fn as_ref(&self) -> &RsaPrivateKey { &self.inner } } impl AssociatedAlgorithmIdentifier for BlindedSigningKey where D: Digest, { type Params = AnyRef<'static>; const ALGORITHM_IDENTIFIER: AlgorithmIdentifierRef<'static> = pkcs1::ALGORITHM_ID; } impl DynSignatureAlgorithmIdentifier for BlindedSigningKey where D: Digest + AssociatedOid, { fn signature_algorithm_identifier(&self) -> pkcs8::spki::Result { get_pss_signature_algo_id::(self.salt_len as u8) } } impl EncodePrivateKey for BlindedSigningKey where D: Digest, { fn to_pkcs8_der(&self) -> pkcs8::Result { self.inner.to_pkcs8_der() } } impl From for BlindedSigningKey where D: Digest, { fn from(key: RsaPrivateKey) -> Self { Self::new(key) } } impl From> for RsaPrivateKey where D: Digest, { fn from(key: BlindedSigningKey) -> Self { key.inner } } impl Keypair for BlindedSigningKey where D: Digest, { type VerifyingKey = VerifyingKey; fn verifying_key(&self) -> Self::VerifyingKey { VerifyingKey { inner: self.inner.to_public_key(), salt_len: self.salt_len, phantom: Default::default(), } } } impl ZeroizeOnDrop for BlindedSigningKey where D: Digest {} rsa-0.9.7/src/pss/signature.rs000064400000000000000000000041121046102023000143670ustar 00000000000000pub use ::signature::{ hazmat::{PrehashSigner, PrehashVerifier}, DigestSigner, DigestVerifier, Error, Keypair, RandomizedDigestSigner, RandomizedSigner, Result, SignatureEncoding, Signer, Verifier, }; use spki::{ der::{asn1::BitString, Result as DerResult}, SignatureBitStringEncoding, }; use crate::algorithms::pad::uint_to_be_pad; use alloc::{boxed::Box, string::ToString}; use core::fmt::{Debug, Display, Formatter, LowerHex, UpperHex}; use num_bigint::BigUint; /// RSASSA-PSS signatures as described in [RFC8017 § 8.1]. /// /// [RFC8017 § 8.1]: https://datatracker.ietf.org/doc/html/rfc8017#section-8.1 #[derive(Clone, PartialEq, Eq)] pub struct Signature { pub(super) inner: BigUint, pub(super) len: usize, } impl SignatureEncoding for Signature { type Repr = Box<[u8]>; } impl SignatureBitStringEncoding for Signature { fn to_bitstring(&self) -> DerResult { BitString::new(0, self.to_vec()) } } impl TryFrom<&[u8]> for Signature { type Error = signature::Error; fn try_from(bytes: &[u8]) -> signature::Result { Ok(Self { len: bytes.len(), inner: BigUint::from_bytes_be(bytes), }) } } impl From for Box<[u8]> { fn from(signature: Signature) -> Box<[u8]> { uint_to_be_pad(signature.inner, signature.len) .expect("RSASSA-PKCS1-v1_5 length invariants should've been enforced") .into_boxed_slice() } } impl Debug for Signature { fn fmt(&self, fmt: &mut Formatter<'_>) -> core::result::Result<(), core::fmt::Error> { fmt.debug_tuple("Signature") .field(&self.to_string()) .finish() } } impl LowerHex for Signature { fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result { write!(f, "{:x}", &self.inner) } } impl UpperHex for Signature { fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result { write!(f, "{:X}", &self.inner) } } impl Display for Signature { fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result { write!(f, "{:X}", self) } } rsa-0.9.7/src/pss/signing_key.rs000064400000000000000000000125271046102023000147050ustar 00000000000000use super::{get_pss_signature_algo_id, sign_digest, Signature, VerifyingKey}; use crate::{Result, RsaPrivateKey}; use const_oid::AssociatedOid; use core::marker::PhantomData; use digest::{Digest, FixedOutputReset}; use pkcs8::{ spki::{ der::AnyRef, AlgorithmIdentifierOwned, AlgorithmIdentifierRef, AssociatedAlgorithmIdentifier, DynSignatureAlgorithmIdentifier, }, EncodePrivateKey, SecretDocument, }; use rand_core::CryptoRngCore; use signature::{ hazmat::RandomizedPrehashSigner, Keypair, RandomizedDigestSigner, RandomizedSigner, }; use zeroize::ZeroizeOnDrop; #[cfg(feature = "getrandom")] use { rand_core::OsRng, signature::{hazmat::PrehashSigner, Signer}, }; /// Signing key for producing RSASSA-PSS signatures as described in /// [RFC8017 § 8.1]. /// /// [RFC8017 § 8.1]: https://datatracker.ietf.org/doc/html/rfc8017#section-8.1 #[derive(Debug, Clone)] pub struct SigningKey where D: Digest, { inner: RsaPrivateKey, salt_len: usize, phantom: PhantomData, } impl SigningKey where D: Digest, { /// Create a new RSASSA-PSS signing key. /// Digest output size is used as a salt length. pub fn new(key: RsaPrivateKey) -> Self { Self::new_with_salt_len(key, ::output_size()) } /// Create a new RSASSA-PSS signing key with a salt of the given length. pub fn new_with_salt_len(key: RsaPrivateKey, salt_len: usize) -> Self { Self { inner: key, salt_len, phantom: Default::default(), } } /// Generate a new random RSASSA-PSS signing key. /// Digest output size is used as a salt length. pub fn random(rng: &mut R, bit_size: usize) -> Result { Self::random_with_salt_len(rng, bit_size, ::output_size()) } /// Generate a new random RSASSA-PSS signing key with a salt of the given length. pub fn random_with_salt_len( rng: &mut R, bit_size: usize, salt_len: usize, ) -> Result { Ok(Self { inner: RsaPrivateKey::new(rng, bit_size)?, salt_len, phantom: Default::default(), }) } /// Return specified salt length for this key pub fn salt_len(&self) -> usize { self.salt_len } } // // `*Signer` trait impls // impl RandomizedDigestSigner for SigningKey where D: Digest + FixedOutputReset, { fn try_sign_digest_with_rng( &self, rng: &mut impl CryptoRngCore, digest: D, ) -> signature::Result { sign_digest::<_, D>(rng, false, &self.inner, &digest.finalize(), self.salt_len)? .as_slice() .try_into() } } impl RandomizedSigner for SigningKey where D: Digest + FixedOutputReset, { fn try_sign_with_rng( &self, rng: &mut impl CryptoRngCore, msg: &[u8], ) -> signature::Result { self.try_sign_digest_with_rng(rng, D::new_with_prefix(msg)) } } impl RandomizedPrehashSigner for SigningKey where D: Digest + FixedOutputReset, { fn sign_prehash_with_rng( &self, rng: &mut impl CryptoRngCore, prehash: &[u8], ) -> signature::Result { sign_digest::<_, D>(rng, false, &self.inner, prehash, self.salt_len)? .as_slice() .try_into() } } #[cfg(feature = "getrandom")] impl PrehashSigner for SigningKey where D: Digest + FixedOutputReset, { fn sign_prehash(&self, prehash: &[u8]) -> signature::Result { self.sign_prehash_with_rng(&mut OsRng, prehash) } } #[cfg(feature = "getrandom")] impl Signer for SigningKey where D: Digest + FixedOutputReset, { fn try_sign(&self, msg: &[u8]) -> signature::Result { self.try_sign_with_rng(&mut OsRng, msg) } } // // Other trait impls // impl AsRef for SigningKey where D: Digest, { fn as_ref(&self) -> &RsaPrivateKey { &self.inner } } impl AssociatedAlgorithmIdentifier for SigningKey where D: Digest, { type Params = AnyRef<'static>; const ALGORITHM_IDENTIFIER: AlgorithmIdentifierRef<'static> = pkcs1::ALGORITHM_ID; } impl DynSignatureAlgorithmIdentifier for SigningKey where D: Digest + AssociatedOid, { fn signature_algorithm_identifier(&self) -> pkcs8::spki::Result { get_pss_signature_algo_id::(self.salt_len as u8) } } impl EncodePrivateKey for SigningKey where D: Digest, { fn to_pkcs8_der(&self) -> pkcs8::Result { self.inner.to_pkcs8_der() } } impl From for SigningKey where D: Digest, { fn from(key: RsaPrivateKey) -> Self { Self::new(key) } } impl From> for RsaPrivateKey where D: Digest, { fn from(key: SigningKey) -> Self { key.inner } } impl Keypair for SigningKey where D: Digest, { type VerifyingKey = VerifyingKey; fn verifying_key(&self) -> Self::VerifyingKey { VerifyingKey { inner: self.inner.to_public_key(), salt_len: self.salt_len, phantom: Default::default(), } } } impl ZeroizeOnDrop for SigningKey where D: Digest {} rsa-0.9.7/src/pss/verifying_key.rs000064400000000000000000000067721046102023000152560ustar 00000000000000use super::{verify_digest, Signature}; use crate::RsaPublicKey; use core::marker::PhantomData; use digest::{Digest, FixedOutputReset}; use pkcs8::{ spki::{der::AnyRef, AlgorithmIdentifierRef, AssociatedAlgorithmIdentifier}, Document, EncodePublicKey, }; use signature::{hazmat::PrehashVerifier, DigestVerifier, Verifier}; /// Verifying key for checking the validity of RSASSA-PSS signatures as /// described in [RFC8017 § 8.1]. /// /// [RFC8017 § 8.1]: https://datatracker.ietf.org/doc/html/rfc8017#section-8.1 #[derive(Debug)] pub struct VerifyingKey where D: Digest, { pub(super) inner: RsaPublicKey, pub(super) salt_len: usize, pub(super) phantom: PhantomData, } impl VerifyingKey where D: Digest, { /// Create a new RSASSA-PSS verifying key. /// Digest output size is used as a salt length. pub fn new(key: RsaPublicKey) -> Self { Self::new_with_salt_len(key, ::output_size()) } /// Create a new RSASSA-PSS verifying key. pub fn new_with_salt_len(key: RsaPublicKey, salt_len: usize) -> Self { Self { inner: key, salt_len, phantom: Default::default(), } } } // // `*Verifier` trait impls // impl DigestVerifier for VerifyingKey where D: Digest + FixedOutputReset, { fn verify_digest(&self, digest: D, signature: &Signature) -> signature::Result<()> { verify_digest::( &self.inner, &digest.finalize(), &signature.inner, signature.len, self.salt_len, ) .map_err(|e| e.into()) } } impl PrehashVerifier for VerifyingKey where D: Digest + FixedOutputReset, { fn verify_prehash(&self, prehash: &[u8], signature: &Signature) -> signature::Result<()> { verify_digest::( &self.inner, prehash, &signature.inner, signature.len, self.salt_len, ) .map_err(|e| e.into()) } } impl Verifier for VerifyingKey where D: Digest + FixedOutputReset, { fn verify(&self, msg: &[u8], signature: &Signature) -> signature::Result<()> { verify_digest::( &self.inner, &D::digest(msg), &signature.inner, signature.len, self.salt_len, ) .map_err(|e| e.into()) } } // // Other trait impls // impl AsRef for VerifyingKey where D: Digest, { fn as_ref(&self) -> &RsaPublicKey { &self.inner } } impl AssociatedAlgorithmIdentifier for VerifyingKey where D: Digest, { type Params = AnyRef<'static>; const ALGORITHM_IDENTIFIER: AlgorithmIdentifierRef<'static> = pkcs1::ALGORITHM_ID; } // Implemented manually so we don't have to bind D with Clone impl Clone for VerifyingKey where D: Digest, { fn clone(&self) -> Self { Self { inner: self.inner.clone(), salt_len: self.salt_len, phantom: Default::default(), } } } impl EncodePublicKey for VerifyingKey where D: Digest, { fn to_public_key_der(&self) -> pkcs8::spki::Result { self.inner.to_public_key_der() } } impl From for VerifyingKey where D: Digest, { fn from(key: RsaPublicKey) -> Self { Self::new(key) } } impl From> for RsaPublicKey where D: Digest, { fn from(key: VerifyingKey) -> Self { key.inner } } rsa-0.9.7/src/pss.rs000064400000000000000000000475311046102023000124020ustar 00000000000000//! Support for the [Probabilistic Signature Scheme] (PSS) a.k.a. RSASSA-PSS. //! //! Designed by Mihir Bellare and Phillip Rogaway. Specified in [RFC8017 § 8.1]. //! //! # Usage //! //! See [code example in the toplevel rustdoc](../index.html#pss-signatures). //! //! [Probabilistic Signature Scheme]: https://en.wikipedia.org/wiki/Probabilistic_signature_scheme //! [RFC8017 § 8.1]: https://datatracker.ietf.org/doc/html/rfc8017#section-8.1 mod blinded_signing_key; mod signature; mod signing_key; mod verifying_key; pub use self::{ blinded_signing_key::BlindedSigningKey, signature::Signature, signing_key::SigningKey, verifying_key::VerifyingKey, }; use alloc::{boxed::Box, vec::Vec}; use core::fmt::{self, Debug}; use const_oid::{AssociatedOid, ObjectIdentifier}; use digest::{Digest, DynDigest, FixedOutputReset}; use num_bigint::BigUint; use pkcs1::RsaPssParams; use pkcs8::spki::{der::Any, AlgorithmIdentifierOwned}; use rand_core::CryptoRngCore; use crate::algorithms::pad::{uint_to_be_pad, uint_to_zeroizing_be_pad}; use crate::algorithms::pss::*; use crate::algorithms::rsa::{rsa_decrypt_and_check, rsa_encrypt}; use crate::errors::{Error, Result}; use crate::traits::PublicKeyParts; use crate::traits::SignatureScheme; use crate::{RsaPrivateKey, RsaPublicKey}; /// Digital signatures using PSS padding. pub struct Pss { /// Create blinded signatures. pub blinded: bool, /// Digest type to use. pub digest: Box, /// Salt length. pub salt_len: usize, } impl Pss { /// New PSS padding for the given digest. /// Digest output size is used as a salt length. pub fn new() -> Self { Self::new_with_salt::(::output_size()) } /// New PSS padding for the given digest with a salt value of the given length. pub fn new_with_salt(len: usize) -> Self { Self { blinded: false, digest: Box::new(T::new()), salt_len: len, } } /// New PSS padding for blinded signatures (RSA-BSSA) for the given digest. /// Digest output size is used as a salt length. pub fn new_blinded() -> Self { Self::new_blinded_with_salt::(::output_size()) } /// New PSS padding for blinded signatures (RSA-BSSA) for the given digest /// with a salt value of the given length. pub fn new_blinded_with_salt( len: usize, ) -> Self { Self { blinded: true, digest: Box::new(T::new()), salt_len: len, } } } impl SignatureScheme for Pss { fn sign( mut self, rng: Option<&mut Rng>, priv_key: &RsaPrivateKey, hashed: &[u8], ) -> Result> { sign( rng.ok_or(Error::InvalidPaddingScheme)?, self.blinded, priv_key, hashed, self.salt_len, &mut *self.digest, ) } fn verify(mut self, pub_key: &RsaPublicKey, hashed: &[u8], sig: &[u8]) -> Result<()> { verify( pub_key, hashed, &BigUint::from_bytes_be(sig), sig.len(), &mut *self.digest, self.salt_len, ) } } impl Debug for Pss { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("PSS") .field("blinded", &self.blinded) .field("digest", &"...") .field("salt_len", &self.salt_len) .finish() } } pub(crate) fn verify( pub_key: &RsaPublicKey, hashed: &[u8], sig: &BigUint, sig_len: usize, digest: &mut dyn DynDigest, salt_len: usize, ) -> Result<()> { if sig_len != pub_key.size() { return Err(Error::Verification); } let mut em = uint_to_be_pad(rsa_encrypt(pub_key, sig)?, pub_key.size())?; emsa_pss_verify(hashed, &mut em, salt_len, digest, pub_key.n().bits()) } pub(crate) fn verify_digest( pub_key: &RsaPublicKey, hashed: &[u8], sig: &BigUint, sig_len: usize, salt_len: usize, ) -> Result<()> where D: Digest + FixedOutputReset, { if sig >= pub_key.n() || sig_len != pub_key.size() { return Err(Error::Verification); } let mut em = uint_to_be_pad(rsa_encrypt(pub_key, sig)?, pub_key.size())?; emsa_pss_verify_digest::(hashed, &mut em, salt_len, pub_key.n().bits()) } /// SignPSS calculates the signature of hashed using RSASSA-PSS. /// /// Note that hashed must be the result of hashing the input message using the /// given hash function. The opts argument may be nil, in which case sensible /// defaults are used. pub(crate) fn sign( rng: &mut T, blind: bool, priv_key: &RsaPrivateKey, hashed: &[u8], salt_len: usize, digest: &mut dyn DynDigest, ) -> Result> { let mut salt = vec![0; salt_len]; rng.fill_bytes(&mut salt[..]); sign_pss_with_salt(blind.then_some(rng), priv_key, hashed, &salt, digest) } pub(crate) fn sign_digest( rng: &mut T, blind: bool, priv_key: &RsaPrivateKey, hashed: &[u8], salt_len: usize, ) -> Result> { let mut salt = vec![0; salt_len]; rng.fill_bytes(&mut salt[..]); sign_pss_with_salt_digest::<_, D>(blind.then_some(rng), priv_key, hashed, &salt) } /// signPSSWithSalt calculates the signature of hashed using PSS with specified salt. /// /// Note that hashed must be the result of hashing the input message using the /// given hash function. salt is a random sequence of bytes whose length will be /// later used to verify the signature. fn sign_pss_with_salt( blind_rng: Option<&mut T>, priv_key: &RsaPrivateKey, hashed: &[u8], salt: &[u8], digest: &mut dyn DynDigest, ) -> Result> { let em_bits = priv_key.n().bits() - 1; let em = emsa_pss_encode(hashed, em_bits, salt, digest)?; uint_to_zeroizing_be_pad( rsa_decrypt_and_check(priv_key, blind_rng, &BigUint::from_bytes_be(&em))?, priv_key.size(), ) } fn sign_pss_with_salt_digest( blind_rng: Option<&mut T>, priv_key: &RsaPrivateKey, hashed: &[u8], salt: &[u8], ) -> Result> { let em_bits = priv_key.n().bits() - 1; let em = emsa_pss_encode_digest::(hashed, em_bits, salt)?; uint_to_zeroizing_be_pad( rsa_decrypt_and_check(priv_key, blind_rng, &BigUint::from_bytes_be(&em))?, priv_key.size(), ) } /// Returns the [`AlgorithmIdentifierOwned`] associated with PSS signature using a given digest. pub fn get_default_pss_signature_algo_id() -> pkcs8::spki::Result where D: Digest + AssociatedOid, { let salt_len: u8 = ::output_size() as u8; get_pss_signature_algo_id::(salt_len) } fn get_pss_signature_algo_id(salt_len: u8) -> pkcs8::spki::Result where D: Digest + AssociatedOid, { const ID_RSASSA_PSS: ObjectIdentifier = ObjectIdentifier::new_unwrap("1.2.840.113549.1.1.10"); let pss_params = RsaPssParams::new::(salt_len); Ok(AlgorithmIdentifierOwned { oid: ID_RSASSA_PSS, parameters: Some(Any::encode_from(&pss_params)?), }) } #[cfg(test)] mod test { use crate::pss::{BlindedSigningKey, Pss, Signature, SigningKey, VerifyingKey}; use crate::{RsaPrivateKey, RsaPublicKey}; use hex_literal::hex; use num_bigint::BigUint; use num_traits::{FromPrimitive, Num}; use rand_chacha::{rand_core::SeedableRng, ChaCha8Rng}; use sha1::{Digest, Sha1}; use signature::hazmat::{PrehashVerifier, RandomizedPrehashSigner}; use signature::{DigestVerifier, Keypair, RandomizedDigestSigner, RandomizedSigner, Verifier}; fn get_private_key() -> RsaPrivateKey { // In order to generate new test vectors you'll need the PEM form of this key: // -----BEGIN RSA PRIVATE KEY----- // MIIBOgIBAAJBALKZD0nEffqM1ACuak0bijtqE2QrI/KLADv7l3kK3ppMyCuLKoF0 // fd7Ai2KW5ToIwzFofvJcS/STa6HA5gQenRUCAwEAAQJBAIq9amn00aS0h/CrjXqu // /ThglAXJmZhOMPVn4eiu7/ROixi9sex436MaVeMqSNf7Ex9a8fRNfWss7Sqd9eWu // RTUCIQDasvGASLqmjeffBNLTXV2A5g4t+kLVCpsEIZAycV5GswIhANEPLmax0ME/ // EO+ZJ79TJKN5yiGBRsv5yvx5UiHxajEXAiAhAol5N4EUyq6I9w1rYdhPMGpLfk7A // IU2snfRJ6Nq2CQIgFrPsWRCkV+gOYcajD17rEqmuLrdIRexpg8N1DOSXoJ8CIGlS // tAboUGBxTDq3ZroNism3DaMIbKPyYrAqhKov1h5V // -----END RSA PRIVATE KEY----- RsaPrivateKey::from_components( BigUint::from_str_radix("9353930466774385905609975137998169297361893554149986716853295022578535724979677252958524466350471210367835187480748268864277464700638583474144061408845077", 10).unwrap(), BigUint::from_u64(65537).unwrap(), BigUint::from_str_radix("7266398431328116344057699379749222532279343923819063639497049039389899328538543087657733766554155839834519529439851673014800261285757759040931985506583861", 10).unwrap(), vec![ BigUint::from_str_radix("98920366548084643601728869055592650835572950932266967461790948584315647051443",10).unwrap(), BigUint::from_str_radix("94560208308847015747498523884063394671606671904944666360068158221458669711639", 10).unwrap() ], ).unwrap() } #[test] fn test_verify_pss() { let priv_key = get_private_key(); let tests = [ ( "test\n", hex!( "6f86f26b14372b2279f79fb6807c49889835c204f71e38249b4c5601462da8ae" "30f26ffdd9c13f1c75eee172bebe7b7c89f2f1526c722833b9737d6c172a962f" ), true, ), ( "test\n", hex!( "6f86f26b14372b2279f79fb6807c49889835c204f71e38249b4c5601462da8ae" "30f26ffdd9c13f1c75eee172bebe7b7c89f2f1526c722833b9737d6c172a962e" ), false, ), ]; let pub_key: RsaPublicKey = priv_key.into(); for (text, sig, expected) in &tests { let digest = Sha1::digest(text.as_bytes()).to_vec(); let result = pub_key.verify(Pss::new::(), &digest, sig); match expected { true => result.expect("failed to verify"), false => { result.expect_err("expected verifying error"); } } } } #[test] fn test_verify_pss_signer() { let priv_key = get_private_key(); let tests = [ ( "test\n", hex!( "6f86f26b14372b2279f79fb6807c49889835c204f71e38249b4c5601462da8ae" "30f26ffdd9c13f1c75eee172bebe7b7c89f2f1526c722833b9737d6c172a962f" ), true, ), ( "test\n", hex!( "6f86f26b14372b2279f79fb6807c49889835c204f71e38249b4c5601462da8ae" "30f26ffdd9c13f1c75eee172bebe7b7c89f2f1526c722833b9737d6c172a962e" ), false, ), ]; let pub_key: RsaPublicKey = priv_key.into(); let verifying_key: VerifyingKey = VerifyingKey::new(pub_key); for (text, sig, expected) in &tests { let result = verifying_key.verify( text.as_bytes(), &Signature::try_from(sig.as_slice()).unwrap(), ); match expected { true => result.expect("failed to verify"), false => { result.expect_err("expected verifying error"); } } } } #[test] fn test_verify_pss_digest_signer() { let priv_key = get_private_key(); let tests = [ ( "test\n", hex!( "6f86f26b14372b2279f79fb6807c49889835c204f71e38249b4c5601462da8ae" "30f26ffdd9c13f1c75eee172bebe7b7c89f2f1526c722833b9737d6c172a962f" ), true, ), ( "test\n", hex!( "6f86f26b14372b2279f79fb6807c49889835c204f71e38249b4c5601462da8ae" "30f26ffdd9c13f1c75eee172bebe7b7c89f2f1526c722833b9737d6c172a962e" ), false, ), ]; let pub_key: RsaPublicKey = priv_key.into(); let verifying_key = VerifyingKey::new(pub_key); for (text, sig, expected) in &tests { let mut digest = Sha1::new(); digest.update(text.as_bytes()); let result = verifying_key.verify_digest(digest, &Signature::try_from(sig.as_slice()).unwrap()); match expected { true => result.expect("failed to verify"), false => { result.expect_err("expected verifying error"); } } } } #[test] fn test_sign_and_verify_roundtrip() { let priv_key = get_private_key(); let tests = ["test\n"]; let rng = ChaCha8Rng::from_seed([42; 32]); for test in &tests { let digest = Sha1::digest(test.as_bytes()).to_vec(); let sig = priv_key .sign_with_rng(&mut rng.clone(), Pss::new::(), &digest) .expect("failed to sign"); priv_key .to_public_key() .verify(Pss::new::(), &digest, &sig) .expect("failed to verify"); } } #[test] fn test_sign_blinded_and_verify_roundtrip() { let priv_key = get_private_key(); let tests = ["test\n"]; let rng = ChaCha8Rng::from_seed([42; 32]); for test in &tests { let digest = Sha1::digest(test.as_bytes()).to_vec(); let sig = priv_key .sign_with_rng(&mut rng.clone(), Pss::new_blinded::(), &digest) .expect("failed to sign"); priv_key .to_public_key() .verify(Pss::new::(), &digest, &sig) .expect("failed to verify"); } } #[test] fn test_sign_and_verify_roundtrip_signer() { let priv_key = get_private_key(); let tests = ["test\n"]; let mut rng = ChaCha8Rng::from_seed([42; 32]); let signing_key = SigningKey::::new(priv_key); let verifying_key = signing_key.verifying_key(); for test in &tests { let sig = signing_key.sign_with_rng(&mut rng, test.as_bytes()); verifying_key .verify(test.as_bytes(), &sig) .expect("failed to verify"); } } #[test] fn test_sign_and_verify_roundtrip_blinded_signer() { let priv_key = get_private_key(); let tests = ["test\n"]; let mut rng = ChaCha8Rng::from_seed([42; 32]); let signing_key = BlindedSigningKey::::new(priv_key); let verifying_key = signing_key.verifying_key(); for test in &tests { let sig = signing_key.sign_with_rng(&mut rng, test.as_bytes()); verifying_key .verify(test.as_bytes(), &sig) .expect("failed to verify"); } } #[test] fn test_sign_and_verify_roundtrip_digest_signer() { let priv_key = get_private_key(); let tests = ["test\n"]; let mut rng = ChaCha8Rng::from_seed([42; 32]); let signing_key = SigningKey::new(priv_key); let verifying_key = signing_key.verifying_key(); for test in &tests { let mut digest = Sha1::new(); digest.update(test.as_bytes()); let sig = signing_key.sign_digest_with_rng(&mut rng, digest); let mut digest = Sha1::new(); digest.update(test.as_bytes()); verifying_key .verify_digest(digest, &sig) .expect("failed to verify"); } } #[test] fn test_sign_and_verify_roundtrip_blinded_digest_signer() { let priv_key = get_private_key(); let tests = ["test\n"]; let mut rng = ChaCha8Rng::from_seed([42; 32]); let signing_key = BlindedSigningKey::::new(priv_key); let verifying_key = signing_key.verifying_key(); for test in &tests { let mut digest = Sha1::new(); digest.update(test.as_bytes()); let sig = signing_key.sign_digest_with_rng(&mut rng, digest); let mut digest = Sha1::new(); digest.update(test.as_bytes()); verifying_key .verify_digest(digest, &sig) .expect("failed to verify"); } } #[test] fn test_verify_pss_hazmat() { let priv_key = get_private_key(); let tests = [ ( Sha1::digest("test\n"), hex!( "6f86f26b14372b2279f79fb6807c49889835c204f71e38249b4c5601462da8ae" "30f26ffdd9c13f1c75eee172bebe7b7c89f2f1526c722833b9737d6c172a962f" ), true, ), ( Sha1::digest("test\n"), hex!( "6f86f26b14372b2279f79fb6807c49889835c204f71e38249b4c5601462da8ae" "30f26ffdd9c13f1c75eee172bebe7b7c89f2f1526c722833b9737d6c172a962e" ), false, ), ]; let pub_key: RsaPublicKey = priv_key.into(); let verifying_key = VerifyingKey::::new(pub_key); for (text, sig, expected) in &tests { let result = verifying_key .verify_prehash(text.as_ref(), &Signature::try_from(sig.as_slice()).unwrap()); match expected { true => result.expect("failed to verify"), false => { result.expect_err("expected verifying error"); } } } } #[test] fn test_sign_and_verify_pss_hazmat() { let priv_key = get_private_key(); let tests = [Sha1::digest("test\n")]; let mut rng = ChaCha8Rng::from_seed([42; 32]); let signing_key = SigningKey::::new(priv_key); let verifying_key = signing_key.verifying_key(); for test in &tests { let sig = signing_key .sign_prehash_with_rng(&mut rng, &test) .expect("failed to sign"); verifying_key .verify_prehash(&test, &sig) .expect("failed to verify"); } } #[test] fn test_sign_and_verify_pss_blinded_hazmat() { let priv_key = get_private_key(); let tests = [Sha1::digest("test\n")]; let mut rng = ChaCha8Rng::from_seed([42; 32]); let signing_key = BlindedSigningKey::::new(priv_key); let verifying_key = signing_key.verifying_key(); for test in &tests { let sig = signing_key .sign_prehash_with_rng(&mut rng, &test) .expect("failed to sign"); verifying_key .verify_prehash(&test, &sig) .expect("failed to verify"); } } #[test] // Tests the corner case where the key is multiple of 8 + 1 bits long fn test_sign_and_verify_2049bit_key() { let plaintext = "Hello\n"; let rng = ChaCha8Rng::from_seed([42; 32]); let priv_key = RsaPrivateKey::new(&mut rng.clone(), 2049).unwrap(); let digest = Sha1::digest(plaintext.as_bytes()).to_vec(); let sig = priv_key .sign_with_rng(&mut rng.clone(), Pss::new::(), &digest) .expect("failed to sign"); priv_key .to_public_key() .verify(Pss::new::(), &digest, &sig) .expect("failed to verify"); } } rsa-0.9.7/src/traits/encryption.rs000064400000000000000000000021641046102023000152660ustar 00000000000000//! Encryption-related traits. use alloc::vec::Vec; use rand_core::CryptoRngCore; use crate::errors::Result; /// Encrypt the message using provided random source pub trait RandomizedEncryptor { /// Encrypt the given message. fn encrypt_with_rng( &self, rng: &mut R, msg: &[u8], ) -> Result>; } /// Decrypt the given message pub trait Decryptor { /// Decrypt the given message. fn decrypt(&self, ciphertext: &[u8]) -> Result>; } /// Decrypt the given message using provided random source pub trait RandomizedDecryptor { /// Decrypt the given message. fn decrypt_with_rng( &self, rng: &mut R, ciphertext: &[u8], ) -> Result>; } /// Encryption keypair with an associated encryption key. pub trait EncryptingKeypair { /// Encrypting key type for this keypair. type EncryptingKey: Clone; /// Get the encrypting key which can encrypt messages to be decrypted by /// the decryption key portion of this keypair. fn encrypting_key(&self) -> Self::EncryptingKey; } rsa-0.9.7/src/traits/keys.rs000064400000000000000000000032111046102023000140410ustar 00000000000000//! Traits related to the key components use num_bigint::{BigInt, BigUint}; use zeroize::Zeroize; /// Components of an RSA public key. pub trait PublicKeyParts { /// Returns the modulus of the key. fn n(&self) -> &BigUint; /// Returns the public exponent of the key. fn e(&self) -> &BigUint; /// Returns the modulus size in bytes. Raw signatures and ciphertexts for /// or by this public key will have the same size. fn size(&self) -> usize { (self.n().bits() + 7) / 8 } } /// Components of an RSA private key. pub trait PrivateKeyParts: PublicKeyParts { /// Returns the private exponent of the key. fn d(&self) -> &BigUint; /// Returns the prime factors. fn primes(&self) -> &[BigUint]; /// Returns the precomputed dp value, D mod (P-1) fn dp(&self) -> Option<&BigUint>; /// Returns the precomputed dq value, D mod (Q-1) fn dq(&self) -> Option<&BigUint>; /// Returns the precomputed qinv value, Q^-1 mod P fn qinv(&self) -> Option<&BigInt>; /// Returns an iterator over the CRT Values fn crt_values(&self) -> Option<&[CrtValue]>; } /// Contains the precomputed Chinese remainder theorem values. #[derive(Debug, Clone)] pub struct CrtValue { /// D mod (prime - 1) pub(crate) exp: BigInt, /// R·Coeff ≡ 1 mod Prime. pub(crate) coeff: BigInt, /// product of primes prior to this (inc p and q) pub(crate) r: BigInt, } impl Zeroize for CrtValue { fn zeroize(&mut self) { self.exp.zeroize(); self.coeff.zeroize(); self.r.zeroize(); } } impl Drop for CrtValue { fn drop(&mut self) { self.zeroize(); } } rsa-0.9.7/src/traits/padding.rs000064400000000000000000000025531046102023000145040ustar 00000000000000//! Supported padding schemes. use alloc::vec::Vec; use rand_core::CryptoRngCore; use crate::errors::Result; use crate::key::{RsaPrivateKey, RsaPublicKey}; /// Padding scheme used for encryption. pub trait PaddingScheme { /// Decrypt the given message using the given private key. /// /// If an `rng` is passed, it uses RSA blinding to help mitigate timing /// side-channel attacks. fn decrypt( self, rng: Option<&mut Rng>, priv_key: &RsaPrivateKey, ciphertext: &[u8], ) -> Result>; /// Encrypt the given message using the given public key. fn encrypt( self, rng: &mut Rng, pub_key: &RsaPublicKey, msg: &[u8], ) -> Result>; } /// Digital signature scheme. pub trait SignatureScheme { /// Sign the given digest. fn sign( self, rng: Option<&mut Rng>, priv_key: &RsaPrivateKey, hashed: &[u8], ) -> Result>; /// Verify a signed message. /// /// `hashed` must be the result of hashing the input using the hashing function /// passed in through `hash`. /// /// If the message is valid `Ok(())` is returned, otherwise an `Err` indicating failure. fn verify(self, pub_key: &RsaPublicKey, hashed: &[u8], sig: &[u8]) -> Result<()>; } rsa-0.9.7/src/traits.rs000064400000000000000000000004311046102023000130670ustar 00000000000000//! RSA-related trait definitions. mod encryption; pub(crate) mod keys; mod padding; pub use encryption::{Decryptor, EncryptingKeypair, RandomizedDecryptor, RandomizedEncryptor}; pub use keys::{PrivateKeyParts, PublicKeyParts}; pub use padding::{PaddingScheme, SignatureScheme}; rsa-0.9.7/tests/examples/pkcs1/rsa2048-priv.der000064400000000000000000000022471046102023000172110ustar 000000000000000,Q_>BCp&3H3cGOo6:])9 K9AOG8,v=Syr~ _'^|KOYp("ܼʻ|CɂrEX2&mt:큒шLG9fܗǬE = |W_SOWN0ǻțxRUϰ'ȟN<򈎓2A֕ܐN> _pX%?^)\m8Pa☗[/~̃btd!^"Jip 6Mϻ.?i*gz?^R喎d|n@d-XO_HeQpOVzcd(m%UU+Ovd?x~eĪн'` \J^2uٗ,n}ѓ_n`a+\|ɜ=:$ãt vj}x T%M|LEL eY$۫5Rhn@mcdsva$-N*ep6~naF~eB%<~\1znZnX3ֻ"ܾ׏Nخ܆G<8ᄆCaнj3y@_&4;k-gu~@To$#jFSg`m\> 7Gb6IhSjNi "ߧ@u Rvd{xWwˋ"& O;ӪŐd1_āVv"!BC'h  cnUHSCp]hWɽe xa~.H$ m`T{d??6cI~4/.&ӡ4jֹw NbM]fi%]1|j}5M%B (kMB5XCQ { m>s.I *li@)q2u8׾LƹۥOV4Ĝ'}Fxcnj|hr1Xz˯OQqE/J|r0mcuZH.Qx.(tV$ol?S>1mɘ<*rsa-0.9.7/tests/examples/pkcs1/rsa2048-priv.pem000064400000000000000000000032131046102023000172120ustar 00000000000000-----BEGIN RSA PRIVATE KEY----- MIIEowIBAAKCAQEAtsQsUV8QpqrygsY+2+JCQ6Fw8/omM71IM2N/R8pPbzbgOl0p 78MZGsgPOQ2HSznjD0FPzsH8oO2B5Uftws04LHb2HJAYlz25+lN5cqfHAfa3fgmC 38FfwBkn7l582UtPWZ/wcBOnyCgb3yLcvJrXyrt8QxHJgvWO23ITrUVYszImbXQ6 7YGS0YhMrbixRzmo2tpm3JcIBtnHrEUMsT0NfFdfsZhTT8YbxBvA8FdODgEwx7u/ vf3J9qbi4+Kv8cvqyJuleIRSjVXPsIMnoejIn04APPKIjpMyQdnWlby7rNyQtE4+ CV+jcFjqJbE/Xilcvqxt6DirjFCvYeKYl1uHLwIDAQABAoIBAH7Mg2LA7bB0EWQh XiL3SrnZG6BpAHAM9jaQ5RFNjua9z7suP5YUaSpnegg/FopeUuWWjmQHudl8bg5A ZPgtoLdYoU8XubfUH19I4o1lUXBPVuaeeqn6Yw/HZCjAbSXkVdz8VbesK092ZD/e 0/4V/3irsn5lrMSq0L322yfvYKaRDFxKCF7UMnWrGcHZl6Msbv/OffLRk19uYB7t 4WGhK1zCfKIfgdLJnD0eoI6Q4wU6sJvvpyTe8NDDo8HpdAwNn3YSahSewKp9gHgg VIQlTZUdsHxM+R+2RUwJZYj9WSTbq+s1nKICUmjQBPnWbrPW963BE5utQPFt3mOe EWRzdsECgYEA3MBhJC1Okq+u5yrFE8plufdwNvm9fg5uYUYafvdlQiXsFTx+XDGm FXpuWhP/bheOh1jByzPZ1rvjF57xiZjkIuzcvtePTs/b5fT82K7CydDchkc8qb0W 2dI40h+13e++sUPKYdC9aqjZHzOgl3kOlkDbyRCF3F8mNDujE49rLWcCgYEA0/MU dX5A6VSDb5K+JCNq8vDaBKNGU8GAr2fpYAhtk/3mXLI+/Z0JN0di9ZgeNhhJr2jN 11OU/2pOButpsgnkIo2y36cOQPf5dQpSgXZke3iNDld3osuLIuPNJn/3C087AtOq +w4YxZClZLAxiLCqX8SBVrB2IiFCQ70SJ++n8vkCgYEAzmi3rBsNEA1jblVIh1PF wJhD/bOQ4nBd92iUV8m9jZdl4wl4YX4u/IBI9MMkIG24YIe2VOl7s9Rk5+4/jNg/ 4QQ2998Y6aljxOZJEdZ+3jQELy4m49OhrTRq2ta5t/Z3CMsJTmLe6f9NXWZpr5iK 8iVdHOjtMXxqfYaR2jVNEtsCgYAl9uWUQiAoa037v0I1wO5YQ9IZgJGJUSDWynsg C4JtPs5zji4ASY+sCipsqWnH8MPKGrC8QClxMr51ONe+30yw78a5jvfbpU9Wqpmq vOU0xJwnlH1GeMUcY8eMfOFocjG0yOtYeubvBIDLr0/AFzz9WHp+Z69RX7m53nUR GDlyKQKBgDGZVAbUBiB8rerqNbONBAxfipoa4IJ+ntBrFT2DtoIZNbSzaoK+nVbH kbWMJycaV5PVOh1lfAiZeWCxQz5RcZh/RS8USnxyMG1j4dP/wLcbdasI8uRaSC6Y hFHL5HjhLrIo0HRWySS2b2ztBI2FP1M+MaaGFPHDzm2OyZg85yr3 -----END RSA PRIVATE KEY----- rsa-0.9.7/tests/examples/pkcs1/rsa2048-pub.der000064400000000000000000000004161046102023000170130ustar 000000000000000 ,Q_>BCp&3H3cGOo6:])9 K9AOG8,v=Syr~ _'^|KOYp("ܼʻ|CɂrEX2&mt:큒шLG9fܗǬE = |W_SOWN0ǻțxRUϰ'ȟN<򈎓2A֕ܐN> _pX%?^)\m8Pa☗[/rsa-0.9.7/tests/examples/pkcs1/rsa2048-pub.pem000064400000000000000000000006521046102023000170240ustar 00000000000000-----BEGIN RSA PUBLIC KEY----- MIIBCgKCAQEAtsQsUV8QpqrygsY+2+JCQ6Fw8/omM71IM2N/R8pPbzbgOl0p78MZ GsgPOQ2HSznjD0FPzsH8oO2B5Uftws04LHb2HJAYlz25+lN5cqfHAfa3fgmC38Ff wBkn7l582UtPWZ/wcBOnyCgb3yLcvJrXyrt8QxHJgvWO23ITrUVYszImbXQ67YGS 0YhMrbixRzmo2tpm3JcIBtnHrEUMsT0NfFdfsZhTT8YbxBvA8FdODgEwx7u/vf3J 9qbi4+Kv8cvqyJuleIRSjVXPsIMnoejIn04APPKIjpMyQdnWlby7rNyQtE4+CV+j cFjqJbE/Xilcvqxt6DirjFCvYeKYl1uHLwIDAQAB -----END RSA PUBLIC KEY----- rsa-0.9.7/tests/examples/pkcs1/rsa4096-priv.der000064400000000000000000000044551046102023000172210ustar 000000000000000 )Era~I[0?}?v^T =ٓ @t6cX(t߃!c@wJLu.f7j"8gf,~"jߨbJ˜%cd8 ^k r#Qw9kHuJԧRB% ;6[! lk?v,@aHrl'>pZ6[(sF#)ypx@8aLRQ⥌uN%.#Kbb4l`p8WocA)6ssDEG @`HY..%T5?`!;JEY喝Y¿~>`Dd_6П$=xp{HuPn!wP  9J BAPw ]q Cb@gå m?,ڔCOsy|cw#&(᤮myIsoM}f[,H uQ {#"f@Ü /#MWV'D6kF1'9[~,V*_PWBcoo(cR-[W,'M:.LiF5ibYG幖C4t*EͲqaƍS:ϗ5@` e]ش^QEa"*&d>wW㹏2%Ip߄SDko]^?w'HpVxP&@db%0QNθle8EU/d"3PU_@Srףa.hE`ѧyޖ5$K0ɽ[::3̃ړƒnT$_{⮪d3'$e H4Dk҆"Iis!ÂGQD|k٨IVN'`,[!']D3uWk6Ii3| S suT1!:yB[f[qD2u`c[w%mw-MK a:yS0GD'^7jJHnrgɡ'=j>{Myuc# fSaM1Um%HA-1[!#ǟ]9VS̏eatg$@9nw!+J؞EսYRw 6o.{|܏{Em[<bhlHڰI6Ɓ"F_6}J_'aG)^.\>sDeW,b4FI~Vrp\egfMY2)CBtpc<9R_YT_RԆ+,6TϨ=*R3FvF}_}z״A_Rc$J?QȕHܶ0l4Ԡml{aVw]ۈvuwQ Ὴs\ɏ-w =䍞r Sch WWEWRtF^i,GdZap{2.mlKe$)G@nE@.KnvvOd*v hbu\fc*8@QI;ƠEwUF<֧ow_TYj8BP2*rD4Wt^tP",`qH4}$=i6F8 %~ådq OnDf!%&ch UW7ulĬKZ#ƀ:H?orKZ K)uN g,wCm o Fb24NJE}TO;C9/%//fQAaz ?#MhHE+ʩ@H:go?!'I cyjDM zg*}묜DYitCmsO*-Z'-aDqɘ$0ӝ)PC|^f\yY~c,XޛP!c+ѳU|H5f%]>Xls($$W^ (,4$hr I8)5*g_ ټ <%$yƓm(6^= 6j>HrL$C>;cK8W-r(Szdy:‡*Rh 3-Tٍn kFwgB!= z5P(s-k)gxqp'"^\pºr&}(/7ɍ6rsa-0.9.7/tests/examples/pkcs1/rsa4096-priv.pem000064400000000000000000000062531046102023000172260ustar 00000000000000-----BEGIN RSA PRIVATE KEY----- MIIJKQIBAAKCAgEAp6dFcoEeomF+Sehb1zDd4w8QP32I7j92XlQNPdmTu7C6FAAC hZ0LQIl0NmN/WLgo6nTfgyFjQHf5nUqi1UyjdYUu9ZdmHTcTzh7ztP1qjiICOORn ZoosfuOGHSISrmoevd+oi2LfEPa8957/SsKY+yVj3xuHZDga+bH7DM0IXgJrCtn2 chojUXfQOWtIdUrUp1JCJQqHO/L25+48dd1hPjZbpPMhCmzGa5Ci+j92LKaIQIe2 v4Fh6xRIGfD1cvIfbI4nPnDUWjZbiygZznNGE8wjsBMpoXkB8XB4QDhh9UxSoFHi pYx1wtnYAJG7mAihBsH37LQDThUFi+7HJcX5GdYuqiNLYmKNNGxgu5GecIUdqzhX Hm8O12NBKfmU6jaP7nNz397AREXrykf6IO0VQKhgyUi6vJjaWRyh3i4uJVQO+bfL NT9gITuBSkXTWe+puBHu/wjGWZO/ioXCv+qqftXmtD4YrmBEZM5flhUBNufQn4sk +tQ9eHARjPp7wkh1UG67wyG5d+CGGupQEoYgEh8LOUqc3QpCQRoTUMB3DZddcbAK kENiQMlnoMOlwgoPbed/Pyyv2pTtAUPB9uNPc+DKwnnu63xjdyOisCbIKALhpK66 qIRt+Y55GUmHc+DU8xmVb03jqtAO+5oUfWazrBoB01ss+0jUALDnqA3JdVECAwEA AQKCAgEAn+MJeyMiuQ+rZgbAF6CV6+ZAw5wQC87gLyOPoU2v846eV1aPESftRDYS a5BGMbEn7Dlbs+4SfrgsiNJWKn+1X+2NFFC35OLS839XQmNvzG8omWNSLVtXBggs rfoBwO6ZtNDpJ006mS4Gl0y+AWlGhjVpYqwZWf2b1EfluZaMBUPfG/E0dCrzRc2y +h+TcbDUz2HGjRbWU9jpmdT9OhbPl4o1qkDoYM3OCWVd2LTPGdQUGx6SrV5RqOSl wn+nRWEdkOSdDpKCIiq28SZkPhx3V4gW/OO5jzIdJUnylKRw34RTRGvzb5hd8l7Y /en98wc/sncn30jp4fxwVrx4llCQt4UBJkBkYsglMFHvhONO48POuPlsZYw4vkVV jS9k4p0iM1BVX8Hvoo7B9K+1ukCA8JqGzcNTjBrXyXLm16NhLmhFupr73xnwkGDR p3neljXi0vjgxRC6JMbESzDJvfr4W+kXrsXUOvqxqjrdM8yD2pPKxpIY9qNutH8Z nVQkyV/Z7Xsei+KuqmQzsickExbCDueSZQzrSL/WNERrGdKGtOoXIkmNoaNpcyEO w4JHUaWAjZqu9ZxEnhmlB3z+yhJr2ajdSZZWHU4ns2Cf+CxbGyHmJ4RdRJYbM7h1 1cT6n/NX72vjNklp4TN8kbKaB7mpE83kDOLVUwyQDnN1FoXmVDECggEBANAhOnlC W2ZbcZEYRIiT7DJ1YA9j2/hbd/To6Z7zAvboJZYEj23Kdy3mu/ESTbhLCv5hsDqG BKsAee1T8zBHl60Bs4xE/ielpF43hIOoBLVqSpZ/SPAahm5yHmfkyaEEivaJJ/qk PWqF2T579wdNunl1Y/yr4SMJt2ZTxtthTcIxzFVtnyWsSEGgLTHN8wFbISMH+dDH n+tdOVbOU8yPoWUb5gdh8Z90ZySJ6vnyFUCfOZVud6ghg/H3K7L+3fG5+/xK2J6k RYCd29W9WVJ3mQwL6TZvuy7PewV8wcPcj7d7+EVtB7vJWzwYFfSOYrgUaMPU2dls D0jasEmTvo2R7eUCggEBAM42xoEFIqvl1kZfNusTfaO56kpfHSfGYUcp645eLly4 jj7xpHOiGUS2ZVez3CzkYuS/NEbLSZADflZysXBcuugbZbr5Z6Jm3Bjv6A9Nu/4a WQYyBc4pQ8rfQhzOdK9wY/0ag688Oa+EUl9ZvcH/VIFfUq/R6NSGKyw2VPbPqD3A jiqdUrn4M8ZGr3aURn38X31617RBiV/Lf/vtUmMksBVKFYI/UQfIlUjt3LYdpTCM bMg01KDBbfpsodZ7YaZWd+sXGc0SXQ7w24gC+3bPwXV3vLJRCuKU4b+KkXOiuFwW prUIyY8tdwt/PeSNnnIMU+JjaAtX5xCUEAFXRVcGUv0CggEAdItGzQPlXmmyLEdk iP4b8x1azwNh965we4m42DLH5C6WbWzcS+Rl3CQp9ZIER0BuRYe6QOsuzfqUS9sI gG52doBPZCp2DwloAwIfiAGbsWJ1pdRcqWaRBGOOtyqb5ThAAFFJO8agRXfx8FVG PKa/1qdvd9tfVFlqgzhCUDIqcqWj/+pEhbn1NBpXdF4YxxeadJ1QvCIsYIVxSDR9 JD0BaTa4FkY4IMvzvbglBhUS5X7DpfOXuWQbGHEJ3U9uRJ+ahOn8ZskhyiWbJhLD Y7Ro1SAOVVc3f7za7HWxotVs/JfErEujWvojxoDOOoVIrj9vcslLu74QyQD8WhcL Swb+KQKCAQB1yk4LBp7uZ8PEwMCC+MgsjJbq0ne575RDbQuTb/K1neoKxEa2kmIy oKk0tpVOw0pF9X3r7lTfwU8aHDuEvkM5L+UlLy9mUbDpQahhjXqTxAMUCeDNCT8j E/IUuE1opR9IRSvxHcqpmkDfHEjLFojzuTpnGdUQCG+Cuqo/rRAh7eqHJwRJHCCe 4mN5rWqyrkTxTQkHeuP4Zyp9AeusnBlEn+O3WWl0s7uqQ8xt7nMcTyoYFi1aggLL J+Atvp5hwESRccmYHSQw053ijCmNjVCpQ7LyfF5mXLqyiXlZ/xml6H5jLFjNwx+b 3pvBAK//31DPIQ8eY6CmFJ0r1ujRs9gVAoIBAQCMVXxINei0BmYGpdwlXbw+tfFY bHMomIyOJCQD+Vde+w0oASwGNLckF2itciBJOCkG1jWvyx0qBb3/yAX+ZwOJt/qQ 1l+Ur7wgCNm8DTzO5CXfxyQCBQYDyfV57oUQ7MaTrG3TKDa24V49xSA2ahukr8kd Pkik1nJMpCRD9IA+OxJjSwQ4Vy2OE7xy8agoU7jZ/KYZnXqQq2TZ5595OsKH+aGQ EQgqUmjyCTMtVNmNbhkDCQf9nmuC7xJGd7oIrWeXpEIhPQl6NRxQoIko3XMtaymm z2cG2vXyD3j4cXD7J5QDxSIbXlxwwrqg5ppy4NHzJn29jJvd/yivqS83yY02 -----END RSA PRIVATE KEY----- rsa-0.9.7/tests/examples/pkcs1/rsa4096-pub.der000064400000000000000000000010161046102023000170150ustar 000000000000000 Era~I[0?}?v^T =ٓ @t6cX(t߃!c@wJLu.f7j"8gf,~"jߨbJ˜%cd8 ^k r#Qw9kHuJԧRB% ;6[! lk?v,@aHrl'>pZ6[(sF#)ypx@8aLRQ⥌uN%.#Kbb4l`p8WocA)6ssDEG @`HY..%T5?`!;JEY喝Y¿~>`Dd_6П$=xp{HuPn!wP  9J BAPw ]q Cb@gå m?,ڔCOsy|cw#&(᤮myIsoM}f[,H uQrsa-0.9.7/tests/examples/pkcs1/rsa4096-pub.pem000064400000000000000000000014071046102023000170300ustar 00000000000000-----BEGIN RSA PUBLIC KEY----- MIICCgKCAgEAp6dFcoEeomF+Sehb1zDd4w8QP32I7j92XlQNPdmTu7C6FAAChZ0L QIl0NmN/WLgo6nTfgyFjQHf5nUqi1UyjdYUu9ZdmHTcTzh7ztP1qjiICOORnZoos fuOGHSISrmoevd+oi2LfEPa8957/SsKY+yVj3xuHZDga+bH7DM0IXgJrCtn2choj UXfQOWtIdUrUp1JCJQqHO/L25+48dd1hPjZbpPMhCmzGa5Ci+j92LKaIQIe2v4Fh 6xRIGfD1cvIfbI4nPnDUWjZbiygZznNGE8wjsBMpoXkB8XB4QDhh9UxSoFHipYx1 wtnYAJG7mAihBsH37LQDThUFi+7HJcX5GdYuqiNLYmKNNGxgu5GecIUdqzhXHm8O 12NBKfmU6jaP7nNz397AREXrykf6IO0VQKhgyUi6vJjaWRyh3i4uJVQO+bfLNT9g ITuBSkXTWe+puBHu/wjGWZO/ioXCv+qqftXmtD4YrmBEZM5flhUBNufQn4sk+tQ9 eHARjPp7wkh1UG67wyG5d+CGGupQEoYgEh8LOUqc3QpCQRoTUMB3DZddcbAKkENi QMlnoMOlwgoPbed/Pyyv2pTtAUPB9uNPc+DKwnnu63xjdyOisCbIKALhpK66qIRt +Y55GUmHc+DU8xmVb03jqtAO+5oUfWazrBoB01ss+0jUALDnqA3JdVECAwEAAQ== -----END RSA PUBLIC KEY----- rsa-0.9.7/tests/examples/pkcs8/rsa2048-priv.der000064400000000000000000000023011046102023000172070ustar 0000000000000000  *H 0,Q_>BCp&3H3cGOo6:])9 K9AOG8,v=Syr~ _'^|KOYp("ܼʻ|CɂrEX2&mt:큒шLG9fܗǬE = |W_SOWN0ǻțxRUϰ'ȟN<򈎓2A֕ܐN> _pX%?^)\m8Pa☗[/~̃btd!^"Jip 6Mϻ.?i*gz?^R喎d|n@d-XO_HeQpOVzcd(m%UU+Ovd?x~eĪн'` \J^2uٗ,n}ѓ_n`a+\|ɜ=:$ãt vj}x T%M|LEL eY$۫5Rhn@mcdsva$-N*ep6~naF~eB%<~\1znZnX3ֻ"ܾ׏Nخ܆G<8ᄆCaнj3y@_&4;k-gu~@To$#jFSg`m\> 7Gb6IhSjNi "ߧ@u Rvd{xWwˋ"& O;ӪŐd1_āVv"!BC'h  cnUHSCp]hWɽe xa~.H$ m`T{d??6cI~4/.&ӡ4jֹw NbM]fi%]1|j}5M%B (kMB5XCQ { m>s.I *li@)q2u8׾LƹۥOV4Ĝ'}Fxcnj|hr1Xz˯OQqE/J|r0mcuZH.Qx.(tV$ol?S>1mɘ<*rsa-0.9.7/tests/examples/pkcs8/rsa2048-priv.pem000064400000000000000000000032501046102023000172220ustar 00000000000000-----BEGIN PRIVATE KEY----- MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQC2xCxRXxCmqvKC xj7b4kJDoXDz+iYzvUgzY39Hyk9vNuA6XSnvwxkayA85DYdLOeMPQU/Owfyg7YHl R+3CzTgsdvYckBiXPbn6U3lyp8cB9rd+CYLfwV/AGSfuXnzZS09Zn/BwE6fIKBvf Ity8mtfKu3xDEcmC9Y7bchOtRVizMiZtdDrtgZLRiEytuLFHOaja2mbclwgG2ces RQyxPQ18V1+xmFNPxhvEG8DwV04OATDHu7+9/cn2puLj4q/xy+rIm6V4hFKNVc+w gyeh6MifTgA88oiOkzJB2daVvLus3JC0Tj4JX6NwWOolsT9eKVy+rG3oOKuMUK9h 4piXW4cvAgMBAAECggEAfsyDYsDtsHQRZCFeIvdKudkboGkAcAz2NpDlEU2O5r3P uy4/lhRpKmd6CD8Wil5S5ZaOZAe52XxuDkBk+C2gt1ihTxe5t9QfX0jijWVRcE9W 5p56qfpjD8dkKMBtJeRV3PxVt6wrT3ZkP97T/hX/eKuyfmWsxKrQvfbbJ+9gppEM XEoIXtQydasZwdmXoyxu/8598tGTX25gHu3hYaErXMJ8oh+B0smcPR6gjpDjBTqw m++nJN7w0MOjwel0DA2fdhJqFJ7Aqn2AeCBUhCVNlR2wfEz5H7ZFTAlliP1ZJNur 6zWcogJSaNAE+dZus9b3rcETm61A8W3eY54RZHN2wQKBgQDcwGEkLU6Sr67nKsUT ymW593A2+b1+Dm5hRhp+92VCJewVPH5cMaYVem5aE/9uF46HWMHLM9nWu+MXnvGJ mOQi7Ny+149Oz9vl9PzYrsLJ0NyGRzypvRbZ0jjSH7Xd776xQ8ph0L1qqNkfM6CX eQ6WQNvJEIXcXyY0O6MTj2stZwKBgQDT8xR1fkDpVINvkr4kI2ry8NoEo0ZTwYCv Z+lgCG2T/eZcsj79nQk3R2L1mB42GEmvaM3XU5T/ak4G62myCeQijbLfpw5A9/l1 ClKBdmR7eI0OV3eiy4si480mf/cLTzsC06r7DhjFkKVksDGIsKpfxIFWsHYiIUJD vRIn76fy+QKBgQDOaLesGw0QDWNuVUiHU8XAmEP9s5DicF33aJRXyb2Nl2XjCXhh fi78gEj0wyQgbbhgh7ZU6Xuz1GTn7j+M2D/hBDb33xjpqWPE5kkR1n7eNAQvLibj 06GtNGra1rm39ncIywlOYt7p/01dZmmvmIryJV0c6O0xfGp9hpHaNU0S2wKBgCX2 5ZRCIChrTfu/QjXA7lhD0hmAkYlRINbKeyALgm0+znOOLgBJj6wKKmypacfww8oa sLxAKXEyvnU4177fTLDvxrmO99ulT1aqmaq85TTEnCeUfUZ4xRxjx4x84WhyMbTI 61h65u8EgMuvT8AXPP1Yen5nr1FfubnedREYOXIpAoGAMZlUBtQGIHyt6uo1s40E DF+Kmhrggn6e0GsVPYO2ghk1tLNqgr6dVseRtYwnJxpXk9U6HWV8CJl5YLFDPlFx mH9FLxRKfHIwbWPh0//Atxt1qwjy5FpILpiEUcvkeOEusijQdFbJJLZvbO0EjYU/ Uz4xpoYU8cPObY7JmDznKvc= -----END PRIVATE KEY----- rsa-0.9.7/tests/examples/pkcs8/rsa2048-pub.der000064400000000000000000000004461046102023000170250ustar 000000000000000"0  *H 0 ,Q_>BCp&3H3cGOo6:])9 K9AOG8,v=Syr~ _'^|KOYp("ܼʻ|CɂrEX2&mt:큒шLG9fܗǬE = |W_SOWN0ǻțxRUϰ'ȟN<򈎓2A֕ܐN> _pX%?^)\m8Pa☗[/rsa-0.9.7/tests/examples/pkcs8/rsa2048-pub.pem000064400000000000000000000007031046102023000170300ustar 00000000000000-----BEGIN PUBLIC KEY----- MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAtsQsUV8QpqrygsY+2+JC Q6Fw8/omM71IM2N/R8pPbzbgOl0p78MZGsgPOQ2HSznjD0FPzsH8oO2B5Uftws04 LHb2HJAYlz25+lN5cqfHAfa3fgmC38FfwBkn7l582UtPWZ/wcBOnyCgb3yLcvJrX yrt8QxHJgvWO23ITrUVYszImbXQ67YGS0YhMrbixRzmo2tpm3JcIBtnHrEUMsT0N fFdfsZhTT8YbxBvA8FdODgEwx7u/vf3J9qbi4+Kv8cvqyJuleIRSjVXPsIMnoejI n04APPKIjpMyQdnWlby7rNyQtE4+CV+jcFjqJbE/Xilcvqxt6DirjFCvYeKYl1uH LwIDAQAB -----END PUBLIC KEY----- rsa-0.9.7/tests/examples/pkcs8/rsa2048-sp800-56b-priv.der000064400000000000000000000023011046102023000204510ustar 0000000000000000  *H 0*YK1inS`0eZ4a,;YԤM:j29˒k㱠p}2JtX{'z{ͺ$kHiN:%죋̻nS:LF:`*N/7qAf2I5vG7w ¡Kz͚&4* 2mrP=/ yɣcӗcLB5/eh2h?#s]ʤ3/%YV2 j-i[2ҊVb5!AR(V)ǐQtE1{!+0rCrsa-0.9.7/tests/pkcs1.rs000064400000000000000000000451111046102023000131610ustar 00000000000000//! PKCS#1 encoding tests use hex_literal::hex; use rsa::{ pkcs1::{DecodeRsaPrivateKey, DecodeRsaPublicKey, EncodeRsaPrivateKey, EncodeRsaPublicKey}, traits::{PrivateKeyParts, PublicKeyParts}, RsaPrivateKey, RsaPublicKey, }; #[cfg(feature = "pem")] use rsa::pkcs1::LineEnding; /// RSA-2048 PKCS#1 private key encoded as ASN.1 DER. /// /// Note: this key is extracted from the corresponding `rsa2048-priv.der` /// example key in the `pkcs8` crate. const RSA_2048_PRIV_DER: &[u8] = include_bytes!("examples/pkcs1/rsa2048-priv.der"); /// RSA-4096 PKCS#1 private key encoded as ASN.1 DER const RSA_4096_PRIV_DER: &[u8] = include_bytes!("examples/pkcs1/rsa4096-priv.der"); /// RSA-2048 PKCS#1 public key encoded as ASN.1 DER. /// /// Note: this key is extracted from the corresponding `rsa2048-priv.der` /// example key in the `pkcs8` crate. const RSA_2048_PUB_DER: &[u8] = include_bytes!("examples/pkcs1/rsa2048-pub.der"); /// RSA-4096 PKCS#1 public key encoded as ASN.1 DER const RSA_4096_PUB_DER: &[u8] = include_bytes!("examples/pkcs1/rsa4096-pub.der"); /// RSA-2048 PKCS#1 private key encoded as PEM #[cfg(feature = "pem")] const RSA_2048_PRIV_PEM: &str = include_str!("examples/pkcs1/rsa2048-priv.pem"); /// RSA-4096 PKCS#1 private key encoded as PEM #[cfg(feature = "pem")] const RSA_4096_PRIV_PEM: &str = include_str!("examples/pkcs1/rsa4096-priv.pem"); /// RSA-2048 PKCS#1 public key encoded as PEM #[cfg(feature = "pem")] const RSA_2048_PUB_PEM: &str = include_str!("examples/pkcs1/rsa2048-pub.pem"); /// RSA-4096 PKCS#1 public key encoded as PEM #[cfg(feature = "pem")] const RSA_4096_PUB_PEM: &str = include_str!("examples/pkcs1/rsa4096-pub.pem"); #[test] fn decode_rsa2048_priv_der() { let key = RsaPrivateKey::from_pkcs1_der(RSA_2048_PRIV_DER).unwrap(); // Extracted using: // $ openssl asn1parse -in tests/examples/pkcs1/rsa2048-priv.pem assert_eq!(&key.n().to_bytes_be(), &hex!("B6C42C515F10A6AAF282C63EDBE24243A170F3FA2633BD4833637F47CA4F6F36E03A5D29EFC3191AC80F390D874B39E30F414FCEC1FCA0ED81E547EDC2CD382C76F61C9018973DB9FA537972A7C701F6B77E0982DFC15FC01927EE5E7CD94B4F599FF07013A7C8281BDF22DCBC9AD7CABB7C4311C982F58EDB7213AD4558B332266D743AED8192D1884CADB8B14739A8DADA66DC970806D9C7AC450CB13D0D7C575FB198534FC61BC41BC0F0574E0E0130C7BBBFBDFDC9F6A6E2E3E2AFF1CBEAC89BA57884528D55CFB08327A1E8C89F4E003CF2888E933241D9D695BCBBACDC90B44E3E095FA37058EA25B13F5E295CBEAC6DE838AB8C50AF61E298975B872F")); assert_eq!(&key.e().to_bytes_be(), &hex!("010001")); assert_eq!(&key.d().to_bytes_be(), &hex!("7ECC8362C0EDB0741164215E22F74AB9D91BA06900700CF63690E5114D8EE6BDCFBB2E3F9614692A677A083F168A5E52E5968E6407B9D97C6E0E4064F82DA0B758A14F17B9B7D41F5F48E28D6551704F56E69E7AA9FA630FC76428C06D25E455DCFC55B7AC2B4F76643FDED3FE15FF78ABB27E65ACC4AAD0BDF6DB27EF60A6910C5C4A085ED43275AB19C1D997A32C6EFFCE7DF2D1935F6E601EEDE161A12B5CC27CA21F81D2C99C3D1EA08E90E3053AB09BEFA724DEF0D0C3A3C1E9740C0D9F76126A149EC0AA7D8078205484254D951DB07C4CF91FB6454C096588FD5924DBABEB359CA2025268D004F9D66EB3D6F7ADC1139BAD40F16DDE639E11647376C1")); assert_eq!(&key.primes()[0].to_bytes_be(), &hex!("DCC061242D4E92AFAEE72AC513CA65B9F77036F9BD7E0E6E61461A7EF7654225EC153C7E5C31A6157A6E5A13FF6E178E8758C1CB33D9D6BBE3179EF18998E422ECDCBED78F4ECFDBE5F4FCD8AEC2C9D0DC86473CA9BD16D9D238D21FB5DDEFBEB143CA61D0BD6AA8D91F33A097790E9640DBC91085DC5F26343BA3138F6B2D67")); assert_eq!(&key.primes()[1].to_bytes_be(), &hex!("D3F314757E40E954836F92BE24236AF2F0DA04A34653C180AF67E960086D93FDE65CB23EFD9D09374762F5981E361849AF68CDD75394FF6A4E06EB69B209E4228DB2DFA70E40F7F9750A528176647B788D0E5777A2CB8B22E3CD267FF70B4F3B02D3AAFB0E18C590A564B03188B0AA5FC48156B07622214243BD1227EFA7F2F9")); } #[test] fn decode_rsa4096_priv_der() { let key = RsaPrivateKey::from_pkcs1_der(RSA_4096_PRIV_DER).unwrap(); // Extracted using: // $ openssl asn1parse -in tests/examples/pkcs1/rsa4096-priv.pem assert_eq!(&key.n().to_bytes_be(), &hex!("A7A74572811EA2617E49E85BD730DDE30F103F7D88EE3F765E540D3DD993BBB0BA140002859D0B40897436637F58B828EA74DF8321634077F99D4AA2D54CA375852EF597661D3713CE1EF3B4FD6A8E220238E467668A2C7EE3861D2212AE6A1EBDDFA88B62DF10F6BCF79EFF4AC298FB2563DF1B8764381AF9B1FB0CCD085E026B0AD9F6721A235177D0396B48754AD4A75242250A873BF2F6E7EE3C75DD613E365BA4F3210A6CC66B90A2FA3F762CA6884087B6BF8161EB144819F0F572F21F6C8E273E70D45A365B8B2819CE734613CC23B01329A17901F17078403861F54C52A051E2A58C75C2D9D80091BB9808A106C1F7ECB4034E15058BEEC725C5F919D62EAA234B62628D346C60BB919E70851DAB38571E6F0ED7634129F994EA368FEE7373DFDEC04445EBCA47FA20ED1540A860C948BABC98DA591CA1DE2E2E25540EF9B7CB353F60213B814A45D359EFA9B811EEFF08C65993BF8A85C2BFEAAA7ED5E6B43E18AE604464CE5F96150136E7D09F8B24FAD43D7870118CFA7BC24875506EBBC321B977E0861AEA50128620121F0B394A9CDD0A42411A1350C0770D975D71B00A90436240C967A0C3A5C20A0F6DE77F3F2CAFDA94ED0143C1F6E34F73E0CAC279EEEB7C637723A2B026C82802E1A4AEBAA8846DF98E7919498773E0D4F319956F4DE3AAD00EFB9A147D66B3AC1A01D35B2CFB48D400B0E7A80DC97551")); assert_eq!(&key.e().to_bytes_be(), &hex!("010001")); assert_eq!(&key.d().to_bytes_be(), &hex!("9FE3097B2322B90FAB6606C017A095EBE640C39C100BCEE02F238FA14DAFF38E9E57568F1127ED4436126B904631B127EC395BB3EE127EB82C88D2562A7FB55FED8D1450B7E4E2D2F37F5742636FCC6F289963522D5B5706082CADFA01C0EE99B4D0E9274D3A992E06974CBE01694686356962AC1959FD9BD447E5B9968C0543DF1BF134742AF345CDB2FA1F9371B0D4CF61C68D16D653D8E999D4FD3A16CF978A35AA40E860CDCE09655DD8B4CF19D4141B1E92AD5E51A8E4A5C27FA745611D90E49D0E9282222AB6F126643E1C77578816FCE3B98F321D2549F294A470DF8453446BF36F985DF25ED8FDE9FDF3073FB27727DF48E9E1FC7056BC78965090B7850126406462C8253051EF84E34EE3C3CEB8F96C658C38BE45558D2F64E29D223350555FC1EFA28EC1F4AFB5BA4080F09A86CDC3538C1AD7C972E6D7A3612E6845BA9AFBDF19F09060D1A779DE9635E2D2F8E0C510BA24C6C44B30C9BDFAF85BE917AEC5D43AFAB1AA3ADD33CC83DA93CAC69218F6A36EB47F199D5424C95FD9ED7B1E8BE2AEAA6433B227241316C20EE792650CEB48BFD634446B19D286B4EA1722498DA1A36973210EC3824751A5808D9AAEF59C449E19A5077CFECA126BD9A8DD4996561D4E27B3609FF82C5B1B21E627845D44961B33B875D5C4FA9FF357EF6BE3364969E1337C91B29A07B9A913CDE40CE2D5530C900E73751685E65431")); assert_eq!(&key.primes()[0].to_bytes_be(), &hex!("D0213A79425B665B719118448893EC3275600F63DBF85B77F4E8E99EF302F6E82596048F6DCA772DE6BBF1124DB84B0AFE61B03A8604AB0079ED53F3304797AD01B38C44FE27A5A45E378483A804B56A4A967F48F01A866E721E67E4C9A1048AF68927FAA43D6A85D93E7BF7074DBA797563FCABE12309B76653C6DB614DC231CC556D9F25AC4841A02D31CDF3015B212307F9D0C79FEB5D3956CE53CC8FA1651BE60761F19F74672489EAF9F215409F39956E77A82183F1F72BB2FEDDF1B9FBFC4AD89EA445809DDBD5BD595277990C0BE9366FBB2ECF7B057CC1C3DC8FB77BF8456D07BBC95B3C1815F48E62B81468C3D4D9D96C0F48DAB04993BE8D91EDE5")); assert_eq!(&key.primes()[1].to_bytes_be(), &hex!("CE36C6810522ABE5D6465F36EB137DA3B9EA4A5F1D27C6614729EB8E5E2E5CB88E3EF1A473A21944B66557B3DC2CE462E4BF3446CB4990037E5672B1705CBAE81B65BAF967A266DC18EFE80F4DBBFE1A59063205CE2943CADF421CCE74AF7063FD1A83AF3C39AF84525F59BDC1FF54815F52AFD1E8D4862B2C3654F6CFA83DC08E2A9D52B9F833C646AF7694467DFC5F7D7AD7B441895FCB7FFBED526324B0154A15823F5107C89548EDDCB61DA5308C6CC834D4A0C16DFA6CA1D67B61A65677EB1719CD125D0EF0DB8802FB76CFC17577BCB2510AE294E1BF8A9173A2B85C16A6B508C98F2D770B7F3DE48D9E720C53E263680B57E7109410015745570652FD")); } #[test] fn decode_rsa2048_pub_der() { let key = RsaPublicKey::from_pkcs1_der(RSA_2048_PUB_DER).unwrap(); // Extracted using: // $ openssl asn1parse -in tests/examples/pkcs1/rsa2048-pub.pem assert_eq!(&key.n().to_bytes_be(), &hex!("B6C42C515F10A6AAF282C63EDBE24243A170F3FA2633BD4833637F47CA4F6F36E03A5D29EFC3191AC80F390D874B39E30F414FCEC1FCA0ED81E547EDC2CD382C76F61C9018973DB9FA537972A7C701F6B77E0982DFC15FC01927EE5E7CD94B4F599FF07013A7C8281BDF22DCBC9AD7CABB7C4311C982F58EDB7213AD4558B332266D743AED8192D1884CADB8B14739A8DADA66DC970806D9C7AC450CB13D0D7C575FB198534FC61BC41BC0F0574E0E0130C7BBBFBDFDC9F6A6E2E3E2AFF1CBEAC89BA57884528D55CFB08327A1E8C89F4E003CF2888E933241D9D695BCBBACDC90B44E3E095FA37058EA25B13F5E295CBEAC6DE838AB8C50AF61E298975B872F")); assert_eq!(&key.e().to_bytes_be(), &hex!("010001")); } #[test] fn decode_rsa4096_pub_der() { let key = RsaPublicKey::from_pkcs1_der(RSA_4096_PUB_DER).unwrap(); // Extracted using: // $ openssl asn1parse -in tests/examples/pkcs1/rsa4096-pub.pem assert_eq!(&key.n().to_bytes_be(), &hex!("A7A74572811EA2617E49E85BD730DDE30F103F7D88EE3F765E540D3DD993BBB0BA140002859D0B40897436637F58B828EA74DF8321634077F99D4AA2D54CA375852EF597661D3713CE1EF3B4FD6A8E220238E467668A2C7EE3861D2212AE6A1EBDDFA88B62DF10F6BCF79EFF4AC298FB2563DF1B8764381AF9B1FB0CCD085E026B0AD9F6721A235177D0396B48754AD4A75242250A873BF2F6E7EE3C75DD613E365BA4F3210A6CC66B90A2FA3F762CA6884087B6BF8161EB144819F0F572F21F6C8E273E70D45A365B8B2819CE734613CC23B01329A17901F17078403861F54C52A051E2A58C75C2D9D80091BB9808A106C1F7ECB4034E15058BEEC725C5F919D62EAA234B62628D346C60BB919E70851DAB38571E6F0ED7634129F994EA368FEE7373DFDEC04445EBCA47FA20ED1540A860C948BABC98DA591CA1DE2E2E25540EF9B7CB353F60213B814A45D359EFA9B811EEFF08C65993BF8A85C2BFEAAA7ED5E6B43E18AE604464CE5F96150136E7D09F8B24FAD43D7870118CFA7BC24875506EBBC321B977E0861AEA50128620121F0B394A9CDD0A42411A1350C0770D975D71B00A90436240C967A0C3A5C20A0F6DE77F3F2CAFDA94ED0143C1F6E34F73E0CAC279EEEB7C637723A2B026C82802E1A4AEBAA8846DF98E7919498773E0D4F319956F4DE3AAD00EFB9A147D66B3AC1A01D35B2CFB48D400B0E7A80DC97551")); assert_eq!(&key.e().to_bytes_be(), &hex!("010001")); } #[test] fn encode_rsa2048_priv_der() { let key = RsaPrivateKey::from_pkcs1_der(RSA_2048_PRIV_DER).unwrap(); let der = key.to_pkcs1_der().unwrap(); assert_eq!(der.as_bytes(), RSA_2048_PRIV_DER) } #[test] fn encode_rsa4096_priv_der() { let key = RsaPrivateKey::from_pkcs1_der(RSA_4096_PRIV_DER).unwrap(); let der = key.to_pkcs1_der().unwrap(); assert_eq!(der.as_bytes(), RSA_4096_PRIV_DER) } #[test] fn encode_rsa2048_pub_der() { let key = RsaPublicKey::from_pkcs1_der(RSA_2048_PUB_DER).unwrap(); let der = key.to_pkcs1_der().unwrap(); assert_eq!(der.as_ref(), RSA_2048_PUB_DER) } #[test] fn encode_rsa4096_pub_der() { let key = RsaPublicKey::from_pkcs1_der(RSA_4096_PUB_DER).unwrap(); let der = key.to_pkcs1_der().unwrap(); assert_eq!(der.as_ref(), RSA_4096_PUB_DER) } #[test] #[cfg(feature = "pem")] fn decode_rsa2048_priv_pem() { let key = RsaPrivateKey::from_pkcs1_pem(RSA_2048_PRIV_PEM).unwrap(); // Extracted using: // $ openssl asn1parse -in tests/examples/pkcs1/rsa2048-priv.pem assert_eq!(&key.n().to_bytes_be(), &hex!("B6C42C515F10A6AAF282C63EDBE24243A170F3FA2633BD4833637F47CA4F6F36E03A5D29EFC3191AC80F390D874B39E30F414FCEC1FCA0ED81E547EDC2CD382C76F61C9018973DB9FA537972A7C701F6B77E0982DFC15FC01927EE5E7CD94B4F599FF07013A7C8281BDF22DCBC9AD7CABB7C4311C982F58EDB7213AD4558B332266D743AED8192D1884CADB8B14739A8DADA66DC970806D9C7AC450CB13D0D7C575FB198534FC61BC41BC0F0574E0E0130C7BBBFBDFDC9F6A6E2E3E2AFF1CBEAC89BA57884528D55CFB08327A1E8C89F4E003CF2888E933241D9D695BCBBACDC90B44E3E095FA37058EA25B13F5E295CBEAC6DE838AB8C50AF61E298975B872F")); assert_eq!(&key.e().to_bytes_be(), &hex!("010001")); assert_eq!(&key.d().to_bytes_be(), &hex!("7ECC8362C0EDB0741164215E22F74AB9D91BA06900700CF63690E5114D8EE6BDCFBB2E3F9614692A677A083F168A5E52E5968E6407B9D97C6E0E4064F82DA0B758A14F17B9B7D41F5F48E28D6551704F56E69E7AA9FA630FC76428C06D25E455DCFC55B7AC2B4F76643FDED3FE15FF78ABB27E65ACC4AAD0BDF6DB27EF60A6910C5C4A085ED43275AB19C1D997A32C6EFFCE7DF2D1935F6E601EEDE161A12B5CC27CA21F81D2C99C3D1EA08E90E3053AB09BEFA724DEF0D0C3A3C1E9740C0D9F76126A149EC0AA7D8078205484254D951DB07C4CF91FB6454C096588FD5924DBABEB359CA2025268D004F9D66EB3D6F7ADC1139BAD40F16DDE639E11647376C1")); assert_eq!(&key.primes()[0].to_bytes_be(), &hex!("DCC061242D4E92AFAEE72AC513CA65B9F77036F9BD7E0E6E61461A7EF7654225EC153C7E5C31A6157A6E5A13FF6E178E8758C1CB33D9D6BBE3179EF18998E422ECDCBED78F4ECFDBE5F4FCD8AEC2C9D0DC86473CA9BD16D9D238D21FB5DDEFBEB143CA61D0BD6AA8D91F33A097790E9640DBC91085DC5F26343BA3138F6B2D67")); assert_eq!(&key.primes()[1].to_bytes_be(), &hex!("D3F314757E40E954836F92BE24236AF2F0DA04A34653C180AF67E960086D93FDE65CB23EFD9D09374762F5981E361849AF68CDD75394FF6A4E06EB69B209E4228DB2DFA70E40F7F9750A528176647B788D0E5777A2CB8B22E3CD267FF70B4F3B02D3AAFB0E18C590A564B03188B0AA5FC48156B07622214243BD1227EFA7F2F9")); } #[test] #[cfg(feature = "pem")] fn decode_rsa4096_priv_pem() { let key = RsaPrivateKey::from_pkcs1_pem(RSA_4096_PRIV_PEM).unwrap(); // Extracted using: // $ openssl asn1parse -in tests/examples/pkcs1/rsa4096-priv.pem assert_eq!(&key.n().to_bytes_be(), &hex!("A7A74572811EA2617E49E85BD730DDE30F103F7D88EE3F765E540D3DD993BBB0BA140002859D0B40897436637F58B828EA74DF8321634077F99D4AA2D54CA375852EF597661D3713CE1EF3B4FD6A8E220238E467668A2C7EE3861D2212AE6A1EBDDFA88B62DF10F6BCF79EFF4AC298FB2563DF1B8764381AF9B1FB0CCD085E026B0AD9F6721A235177D0396B48754AD4A75242250A873BF2F6E7EE3C75DD613E365BA4F3210A6CC66B90A2FA3F762CA6884087B6BF8161EB144819F0F572F21F6C8E273E70D45A365B8B2819CE734613CC23B01329A17901F17078403861F54C52A051E2A58C75C2D9D80091BB9808A106C1F7ECB4034E15058BEEC725C5F919D62EAA234B62628D346C60BB919E70851DAB38571E6F0ED7634129F994EA368FEE7373DFDEC04445EBCA47FA20ED1540A860C948BABC98DA591CA1DE2E2E25540EF9B7CB353F60213B814A45D359EFA9B811EEFF08C65993BF8A85C2BFEAAA7ED5E6B43E18AE604464CE5F96150136E7D09F8B24FAD43D7870118CFA7BC24875506EBBC321B977E0861AEA50128620121F0B394A9CDD0A42411A1350C0770D975D71B00A90436240C967A0C3A5C20A0F6DE77F3F2CAFDA94ED0143C1F6E34F73E0CAC279EEEB7C637723A2B026C82802E1A4AEBAA8846DF98E7919498773E0D4F319956F4DE3AAD00EFB9A147D66B3AC1A01D35B2CFB48D400B0E7A80DC97551")); assert_eq!(&key.e().to_bytes_be(), &hex!("010001")); assert_eq!(&key.d().to_bytes_be(), &hex!("9FE3097B2322B90FAB6606C017A095EBE640C39C100BCEE02F238FA14DAFF38E9E57568F1127ED4436126B904631B127EC395BB3EE127EB82C88D2562A7FB55FED8D1450B7E4E2D2F37F5742636FCC6F289963522D5B5706082CADFA01C0EE99B4D0E9274D3A992E06974CBE01694686356962AC1959FD9BD447E5B9968C0543DF1BF134742AF345CDB2FA1F9371B0D4CF61C68D16D653D8E999D4FD3A16CF978A35AA40E860CDCE09655DD8B4CF19D4141B1E92AD5E51A8E4A5C27FA745611D90E49D0E9282222AB6F126643E1C77578816FCE3B98F321D2549F294A470DF8453446BF36F985DF25ED8FDE9FDF3073FB27727DF48E9E1FC7056BC78965090B7850126406462C8253051EF84E34EE3C3CEB8F96C658C38BE45558D2F64E29D223350555FC1EFA28EC1F4AFB5BA4080F09A86CDC3538C1AD7C972E6D7A3612E6845BA9AFBDF19F09060D1A779DE9635E2D2F8E0C510BA24C6C44B30C9BDFAF85BE917AEC5D43AFAB1AA3ADD33CC83DA93CAC69218F6A36EB47F199D5424C95FD9ED7B1E8BE2AEAA6433B227241316C20EE792650CEB48BFD634446B19D286B4EA1722498DA1A36973210EC3824751A5808D9AAEF59C449E19A5077CFECA126BD9A8DD4996561D4E27B3609FF82C5B1B21E627845D44961B33B875D5C4FA9FF357EF6BE3364969E1337C91B29A07B9A913CDE40CE2D5530C900E73751685E65431")); assert_eq!(&key.primes()[0].to_bytes_be(), &hex!("D0213A79425B665B719118448893EC3275600F63DBF85B77F4E8E99EF302F6E82596048F6DCA772DE6BBF1124DB84B0AFE61B03A8604AB0079ED53F3304797AD01B38C44FE27A5A45E378483A804B56A4A967F48F01A866E721E67E4C9A1048AF68927FAA43D6A85D93E7BF7074DBA797563FCABE12309B76653C6DB614DC231CC556D9F25AC4841A02D31CDF3015B212307F9D0C79FEB5D3956CE53CC8FA1651BE60761F19F74672489EAF9F215409F39956E77A82183F1F72BB2FEDDF1B9FBFC4AD89EA445809DDBD5BD595277990C0BE9366FBB2ECF7B057CC1C3DC8FB77BF8456D07BBC95B3C1815F48E62B81468C3D4D9D96C0F48DAB04993BE8D91EDE5")); assert_eq!(&key.primes()[1].to_bytes_be(), &hex!("CE36C6810522ABE5D6465F36EB137DA3B9EA4A5F1D27C6614729EB8E5E2E5CB88E3EF1A473A21944B66557B3DC2CE462E4BF3446CB4990037E5672B1705CBAE81B65BAF967A266DC18EFE80F4DBBFE1A59063205CE2943CADF421CCE74AF7063FD1A83AF3C39AF84525F59BDC1FF54815F52AFD1E8D4862B2C3654F6CFA83DC08E2A9D52B9F833C646AF7694467DFC5F7D7AD7B441895FCB7FFBED526324B0154A15823F5107C89548EDDCB61DA5308C6CC834D4A0C16DFA6CA1D67B61A65677EB1719CD125D0EF0DB8802FB76CFC17577BCB2510AE294E1BF8A9173A2B85C16A6B508C98F2D770B7F3DE48D9E720C53E263680B57E7109410015745570652FD")); } #[test] #[cfg(feature = "pem")] fn decode_rsa2048_pub_pem() { let key = RsaPublicKey::from_pkcs1_pem(RSA_2048_PUB_PEM).unwrap(); // Extracted using: // $ openssl asn1parse -in tests/examples/pkcs1/rsa2048-pub.pem assert_eq!(&key.n().to_bytes_be(), &hex!("B6C42C515F10A6AAF282C63EDBE24243A170F3FA2633BD4833637F47CA4F6F36E03A5D29EFC3191AC80F390D874B39E30F414FCEC1FCA0ED81E547EDC2CD382C76F61C9018973DB9FA537972A7C701F6B77E0982DFC15FC01927EE5E7CD94B4F599FF07013A7C8281BDF22DCBC9AD7CABB7C4311C982F58EDB7213AD4558B332266D743AED8192D1884CADB8B14739A8DADA66DC970806D9C7AC450CB13D0D7C575FB198534FC61BC41BC0F0574E0E0130C7BBBFBDFDC9F6A6E2E3E2AFF1CBEAC89BA57884528D55CFB08327A1E8C89F4E003CF2888E933241D9D695BCBBACDC90B44E3E095FA37058EA25B13F5E295CBEAC6DE838AB8C50AF61E298975B872F")); assert_eq!(&key.e().to_bytes_be(), &hex!("010001")); } #[test] #[cfg(feature = "pem")] fn decode_rsa4096_pub_pem() { let key = RsaPublicKey::from_pkcs1_pem(RSA_4096_PUB_PEM).unwrap(); // Extracted using: // $ openssl asn1parse -in tests/examples/pkcs1/rsa4096-pub.pem assert_eq!(&key.n().to_bytes_be(), &hex!("A7A74572811EA2617E49E85BD730DDE30F103F7D88EE3F765E540D3DD993BBB0BA140002859D0B40897436637F58B828EA74DF8321634077F99D4AA2D54CA375852EF597661D3713CE1EF3B4FD6A8E220238E467668A2C7EE3861D2212AE6A1EBDDFA88B62DF10F6BCF79EFF4AC298FB2563DF1B8764381AF9B1FB0CCD085E026B0AD9F6721A235177D0396B48754AD4A75242250A873BF2F6E7EE3C75DD613E365BA4F3210A6CC66B90A2FA3F762CA6884087B6BF8161EB144819F0F572F21F6C8E273E70D45A365B8B2819CE734613CC23B01329A17901F17078403861F54C52A051E2A58C75C2D9D80091BB9808A106C1F7ECB4034E15058BEEC725C5F919D62EAA234B62628D346C60BB919E70851DAB38571E6F0ED7634129F994EA368FEE7373DFDEC04445EBCA47FA20ED1540A860C948BABC98DA591CA1DE2E2E25540EF9B7CB353F60213B814A45D359EFA9B811EEFF08C65993BF8A85C2BFEAAA7ED5E6B43E18AE604464CE5F96150136E7D09F8B24FAD43D7870118CFA7BC24875506EBBC321B977E0861AEA50128620121F0B394A9CDD0A42411A1350C0770D975D71B00A90436240C967A0C3A5C20A0F6DE77F3F2CAFDA94ED0143C1F6E34F73E0CAC279EEEB7C637723A2B026C82802E1A4AEBAA8846DF98E7919498773E0D4F319956F4DE3AAD00EFB9A147D66B3AC1A01D35B2CFB48D400B0E7A80DC97551")); assert_eq!(&key.e().to_bytes_be(), &hex!("010001")); } #[test] #[cfg(feature = "pem")] fn encode_rsa2048_priv_pem() { let key = RsaPrivateKey::from_pkcs1_pem(RSA_2048_PRIV_PEM).unwrap(); let pem = key.to_pkcs1_pem(LineEnding::LF).unwrap(); assert_eq!(&*pem, RSA_2048_PRIV_PEM) } #[test] #[cfg(feature = "pem")] fn encode_rsa4096_priv_pem() { let key = RsaPrivateKey::from_pkcs1_pem(RSA_4096_PRIV_PEM).unwrap(); let pem = key.to_pkcs1_pem(LineEnding::LF).unwrap(); assert_eq!(&*pem, RSA_4096_PRIV_PEM) } #[test] #[cfg(feature = "pem")] fn encode_rsa2048_pub_pem() { let key = RsaPublicKey::from_pkcs1_pem(RSA_2048_PUB_PEM).unwrap(); let pem = key.to_pkcs1_pem(LineEnding::LF).unwrap(); assert_eq!(&*pem, RSA_2048_PUB_PEM) } #[test] #[cfg(feature = "pem")] fn encode_rsa4096_pub_pem() { let key = RsaPublicKey::from_pkcs1_pem(RSA_4096_PUB_PEM).unwrap(); let pem = key.to_pkcs1_pem(LineEnding::LF).unwrap(); assert_eq!(&*pem, RSA_4096_PUB_PEM) } rsa-0.9.7/tests/pkcs1v15.rs000064400000000000000000000025361046102023000135210ustar 00000000000000// simple but prevent regression - see https://github.com/RustCrypto/RSA/issues/329 #[cfg(feature = "pem")] #[test] fn signature_stringify() { use pkcs8::DecodePrivateKey; use signature::Signer; use rsa::pkcs1v15::SigningKey; use rsa::RsaPrivateKey; let pem = include_str!("examples/pkcs8/rsa2048-priv.pem"); let private_key = RsaPrivateKey::from_pkcs8_pem(pem).unwrap(); let signing_key = SigningKey::::new(private_key); let bytes: &[u8] = b"rsa4096"; // HACK - the criterion is that the signature has leading zeros. let signature = signing_key.sign(bytes); let expected = "029E365B60971D5A499FF5E1C288B954D3A5DCF52482CEE46DB90DC860B725A8D6CA031146FA156E9F17579BE6122FFB11DAC35E59B2193D75F7B31CE1442DDE7F4FF7885AD5D6080266E9A33BB4CEC93FCC2B6B885457A0ABF19E2DAA00876F694B37F535F119925CCCF9A17B90AE6CF39F07D7FEFBEECDF1B344C14B728196DDD154230BADDEDA5A7EFF373F6CD3EF6D41789572A7A068E3A252D3B7D5D706C6170D8CFDB48C8E738A4B3BFEA3E15716805E376EBD99EA09C6E82F3CFA13CEB23CD289E8F95C27F489ADC05AAACE8A9276EE7CED3B7A5C7264F0D34FF18CEDC3E91D667FCF9992A8CFDE8562F65FDDE1E06595C27E0F82063839A358C927B2"; assert_eq!(format!("{}", signature), expected); assert_eq!(format!("{:x}", signature), expected.to_lowercase()); assert_eq!(format!("{:X}", signature), expected); assert_eq!(signature.to_string(), expected); } rsa-0.9.7/tests/pkcs8.rs000064400000000000000000000177461046102023000132050ustar 00000000000000//! PKCS#8 encoding tests /// RSA-2048 PKCS#8 private key encoded as ASN.1 DER const RSA_2048_PRIV_DER: &[u8] = include_bytes!("examples/pkcs8/rsa2048-priv.der"); /// RSA-2048 `SubjectPublicKeyInfo` encoded as ASN.1 DER const RSA_2048_PUB_DER: &[u8] = include_bytes!("examples/pkcs8/rsa2048-pub.der"); /// RSA-2048 PKCS#8 private key encoded as PEM #[cfg(feature = "pem")] const RSA_2048_PRIV_PEM: &str = include_str!("examples/pkcs8/rsa2048-priv.pem"); /// RSA-2048 PKCS#8 public key encoded as PEM #[cfg(feature = "pem")] const RSA_2048_PUB_PEM: &str = include_str!("examples/pkcs8/rsa2048-pub.pem"); use hex_literal::hex; use rsa::{ pkcs1v15, pkcs8::{DecodePrivateKey, DecodePublicKey, EncodePrivateKey, EncodePublicKey}, traits::{PrivateKeyParts, PublicKeyParts}, RsaPrivateKey, RsaPublicKey, }; use sha2::Sha256; #[cfg(feature = "pem")] use rsa::pkcs8::LineEnding; #[test] fn decode_rsa2048_priv_der() { let key = RsaPrivateKey::from_pkcs8_der(RSA_2048_PRIV_DER).unwrap(); // Note: matches PKCS#1 test vectors assert_eq!(&key.n().to_bytes_be(), &hex!("B6C42C515F10A6AAF282C63EDBE24243A170F3FA2633BD4833637F47CA4F6F36E03A5D29EFC3191AC80F390D874B39E30F414FCEC1FCA0ED81E547EDC2CD382C76F61C9018973DB9FA537972A7C701F6B77E0982DFC15FC01927EE5E7CD94B4F599FF07013A7C8281BDF22DCBC9AD7CABB7C4311C982F58EDB7213AD4558B332266D743AED8192D1884CADB8B14739A8DADA66DC970806D9C7AC450CB13D0D7C575FB198534FC61BC41BC0F0574E0E0130C7BBBFBDFDC9F6A6E2E3E2AFF1CBEAC89BA57884528D55CFB08327A1E8C89F4E003CF2888E933241D9D695BCBBACDC90B44E3E095FA37058EA25B13F5E295CBEAC6DE838AB8C50AF61E298975B872F")); assert_eq!(&key.e().to_bytes_be(), &hex!("010001")); assert_eq!(&key.d().to_bytes_be(), &hex!("7ECC8362C0EDB0741164215E22F74AB9D91BA06900700CF63690E5114D8EE6BDCFBB2E3F9614692A677A083F168A5E52E5968E6407B9D97C6E0E4064F82DA0B758A14F17B9B7D41F5F48E28D6551704F56E69E7AA9FA630FC76428C06D25E455DCFC55B7AC2B4F76643FDED3FE15FF78ABB27E65ACC4AAD0BDF6DB27EF60A6910C5C4A085ED43275AB19C1D997A32C6EFFCE7DF2D1935F6E601EEDE161A12B5CC27CA21F81D2C99C3D1EA08E90E3053AB09BEFA724DEF0D0C3A3C1E9740C0D9F76126A149EC0AA7D8078205484254D951DB07C4CF91FB6454C096588FD5924DBABEB359CA2025268D004F9D66EB3D6F7ADC1139BAD40F16DDE639E11647376C1")); assert_eq!(&key.primes()[0].to_bytes_be(), &hex!("DCC061242D4E92AFAEE72AC513CA65B9F77036F9BD7E0E6E61461A7EF7654225EC153C7E5C31A6157A6E5A13FF6E178E8758C1CB33D9D6BBE3179EF18998E422ECDCBED78F4ECFDBE5F4FCD8AEC2C9D0DC86473CA9BD16D9D238D21FB5DDEFBEB143CA61D0BD6AA8D91F33A097790E9640DBC91085DC5F26343BA3138F6B2D67")); assert_eq!(&key.primes()[1].to_bytes_be(), &hex!("D3F314757E40E954836F92BE24236AF2F0DA04A34653C180AF67E960086D93FDE65CB23EFD9D09374762F5981E361849AF68CDD75394FF6A4E06EB69B209E4228DB2DFA70E40F7F9750A528176647B788D0E5777A2CB8B22E3CD267FF70B4F3B02D3AAFB0E18C590A564B03188B0AA5FC48156B07622214243BD1227EFA7F2F9")); let _ = pkcs1v15::SigningKey::::from_pkcs8_der(RSA_2048_PRIV_DER).unwrap(); } #[test] fn decode_rsa2048_pub_der() { let key = RsaPublicKey::from_public_key_der(RSA_2048_PUB_DER).unwrap(); // Note: matches PKCS#1 test vectors assert_eq!(&key.n().to_bytes_be(), &hex!("B6C42C515F10A6AAF282C63EDBE24243A170F3FA2633BD4833637F47CA4F6F36E03A5D29EFC3191AC80F390D874B39E30F414FCEC1FCA0ED81E547EDC2CD382C76F61C9018973DB9FA537972A7C701F6B77E0982DFC15FC01927EE5E7CD94B4F599FF07013A7C8281BDF22DCBC9AD7CABB7C4311C982F58EDB7213AD4558B332266D743AED8192D1884CADB8B14739A8DADA66DC970806D9C7AC450CB13D0D7C575FB198534FC61BC41BC0F0574E0E0130C7BBBFBDFDC9F6A6E2E3E2AFF1CBEAC89BA57884528D55CFB08327A1E8C89F4E003CF2888E933241D9D695BCBBACDC90B44E3E095FA37058EA25B13F5E295CBEAC6DE838AB8C50AF61E298975B872F")); assert_eq!(&key.e().to_bytes_be(), &hex!("010001")); let _ = pkcs1v15::VerifyingKey::::from_public_key_der(RSA_2048_PUB_DER).unwrap(); } #[test] fn encode_rsa2048_priv_der() { let key = RsaPrivateKey::from_pkcs8_der(RSA_2048_PRIV_DER).unwrap(); let der = key.to_pkcs8_der().unwrap(); assert_eq!(der.as_bytes(), RSA_2048_PRIV_DER); let pkcs1v15_key = pkcs1v15::SigningKey::::from_pkcs8_der(RSA_2048_PRIV_DER).unwrap(); let pkcs1v15_der = pkcs1v15_key.to_pkcs8_der().unwrap(); assert_eq!(pkcs1v15_der.as_bytes(), RSA_2048_PRIV_DER); } #[test] fn encode_rsa2048_pub_der() { let key = RsaPublicKey::from_public_key_der(RSA_2048_PUB_DER).unwrap(); let der = key.to_public_key_der().unwrap(); assert_eq!(der.as_ref(), RSA_2048_PUB_DER); let pkcs1v15_key = pkcs1v15::VerifyingKey::::from_public_key_der(RSA_2048_PUB_DER).unwrap(); let pkcs1v15_der = pkcs1v15_key.to_public_key_der().unwrap(); assert_eq!(pkcs1v15_der.as_ref(), RSA_2048_PUB_DER); } #[test] #[cfg(feature = "pem")] fn decode_rsa2048_priv_pem() { let key = RsaPrivateKey::from_pkcs8_pem(RSA_2048_PRIV_PEM).unwrap(); // Note: matches PKCS#1 test vectors assert_eq!(&key.n().to_bytes_be(), &hex!("B6C42C515F10A6AAF282C63EDBE24243A170F3FA2633BD4833637F47CA4F6F36E03A5D29EFC3191AC80F390D874B39E30F414FCEC1FCA0ED81E547EDC2CD382C76F61C9018973DB9FA537972A7C701F6B77E0982DFC15FC01927EE5E7CD94B4F599FF07013A7C8281BDF22DCBC9AD7CABB7C4311C982F58EDB7213AD4558B332266D743AED8192D1884CADB8B14739A8DADA66DC970806D9C7AC450CB13D0D7C575FB198534FC61BC41BC0F0574E0E0130C7BBBFBDFDC9F6A6E2E3E2AFF1CBEAC89BA57884528D55CFB08327A1E8C89F4E003CF2888E933241D9D695BCBBACDC90B44E3E095FA37058EA25B13F5E295CBEAC6DE838AB8C50AF61E298975B872F")); assert_eq!(&key.e().to_bytes_be(), &hex!("010001")); assert_eq!(&key.d().to_bytes_be(), &hex!("7ECC8362C0EDB0741164215E22F74AB9D91BA06900700CF63690E5114D8EE6BDCFBB2E3F9614692A677A083F168A5E52E5968E6407B9D97C6E0E4064F82DA0B758A14F17B9B7D41F5F48E28D6551704F56E69E7AA9FA630FC76428C06D25E455DCFC55B7AC2B4F76643FDED3FE15FF78ABB27E65ACC4AAD0BDF6DB27EF60A6910C5C4A085ED43275AB19C1D997A32C6EFFCE7DF2D1935F6E601EEDE161A12B5CC27CA21F81D2C99C3D1EA08E90E3053AB09BEFA724DEF0D0C3A3C1E9740C0D9F76126A149EC0AA7D8078205484254D951DB07C4CF91FB6454C096588FD5924DBABEB359CA2025268D004F9D66EB3D6F7ADC1139BAD40F16DDE639E11647376C1")); assert_eq!(&key.primes()[0].to_bytes_be(), &hex!("DCC061242D4E92AFAEE72AC513CA65B9F77036F9BD7E0E6E61461A7EF7654225EC153C7E5C31A6157A6E5A13FF6E178E8758C1CB33D9D6BBE3179EF18998E422ECDCBED78F4ECFDBE5F4FCD8AEC2C9D0DC86473CA9BD16D9D238D21FB5DDEFBEB143CA61D0BD6AA8D91F33A097790E9640DBC91085DC5F26343BA3138F6B2D67")); assert_eq!(&key.primes()[1].to_bytes_be(), &hex!("D3F314757E40E954836F92BE24236AF2F0DA04A34653C180AF67E960086D93FDE65CB23EFD9D09374762F5981E361849AF68CDD75394FF6A4E06EB69B209E4228DB2DFA70E40F7F9750A528176647B788D0E5777A2CB8B22E3CD267FF70B4F3B02D3AAFB0E18C590A564B03188B0AA5FC48156B07622214243BD1227EFA7F2F9")); let _ = pkcs1v15::SigningKey::::from_pkcs8_pem(RSA_2048_PRIV_PEM).unwrap(); } #[test] #[cfg(feature = "pem")] fn decode_rsa2048_pub_pem() { let key = RsaPublicKey::from_public_key_pem(RSA_2048_PUB_PEM).unwrap(); // Note: matches PKCS#1 test vectors assert_eq!(&key.n().to_bytes_be(), &hex!("B6C42C515F10A6AAF282C63EDBE24243A170F3FA2633BD4833637F47CA4F6F36E03A5D29EFC3191AC80F390D874B39E30F414FCEC1FCA0ED81E547EDC2CD382C76F61C9018973DB9FA537972A7C701F6B77E0982DFC15FC01927EE5E7CD94B4F599FF07013A7C8281BDF22DCBC9AD7CABB7C4311C982F58EDB7213AD4558B332266D743AED8192D1884CADB8B14739A8DADA66DC970806D9C7AC450CB13D0D7C575FB198534FC61BC41BC0F0574E0E0130C7BBBFBDFDC9F6A6E2E3E2AFF1CBEAC89BA57884528D55CFB08327A1E8C89F4E003CF2888E933241D9D695BCBBACDC90B44E3E095FA37058EA25B13F5E295CBEAC6DE838AB8C50AF61E298975B872F")); assert_eq!(&key.e().to_bytes_be(), &hex!("010001")); let _ = pkcs1v15::VerifyingKey::::from_public_key_pem(RSA_2048_PUB_PEM).unwrap(); } #[test] #[cfg(feature = "pem")] fn encode_rsa2048_priv_pem() { let key = RsaPrivateKey::from_pkcs8_pem(RSA_2048_PRIV_PEM).unwrap(); let pem = key.to_pkcs8_pem(LineEnding::LF).unwrap(); assert_eq!(&*pem, RSA_2048_PRIV_PEM) } #[test] #[cfg(feature = "pem")] fn encode_rsa2048_pub_pem() { let key = RsaPublicKey::from_public_key_pem(RSA_2048_PUB_PEM).unwrap(); let pem = key.to_public_key_pem(LineEnding::LF).unwrap(); assert_eq!(&*pem, RSA_2048_PUB_PEM) } rsa-0.9.7/tests/proptests.rs000064400000000000000000000027651046102023000142130ustar 00000000000000//! Property-based tests. use proptest::prelude::*; use rand_chacha::ChaCha8Rng; use rand_core::SeedableRng; use rsa::{ pkcs1v15, signature::{Keypair, SignatureEncoding, Signer, Verifier}, RsaPrivateKey, }; use sha2::Sha256; prop_compose! { // WARNING: do *NOT* copy and paste this code. It's insecure and optimized for test speed. fn private_key()(seed in any::<[u8; 32]>()) -> RsaPrivateKey { let mut rng = ChaCha8Rng::from_seed(seed); RsaPrivateKey::new(&mut rng, 512).unwrap() } } proptest! { #[test] fn pkcs1v15_sign_roundtrip(private_key in private_key(), msg in any::>()) { let signing_key = pkcs1v15::SigningKey::::new(private_key); let signature_bytes = signing_key.sign(&msg).to_bytes(); let verifying_key = signing_key.verifying_key(); let signature = pkcs1v15::Signature::try_from(&*signature_bytes).unwrap(); prop_assert!(verifying_key.verify(&msg, &signature).is_ok()); } // TODO(tarcieri): debug why these are failing // #[test] // fn pss_sign_roundtrip(private_key in private_key(), msg in any::>()) { // let signing_key = pss::SigningKey::::new(private_key); // let signature_bytes = signing_key.sign(&msg).to_bytes(); // // let verifying_key = signing_key.verifying_key(); // let signature = pss::Signature::try_from(&*signature_bytes).unwrap(); // prop_assert!(verifying_key.verify(&msg, &signature).is_ok()); // } }