chore: checkpoint before Python removal

This commit is contained in:
2026-03-26 22:33:59 +00:00
parent 683cec9307
commit e568ddf82a
29972 changed files with 11269302 additions and 2 deletions

1
vendor/base64ct/.cargo-checksum.json vendored Normal file
View File

@@ -0,0 +1 @@
{"files":{".cargo_vcs_info.json":"a93e2029c21186a89a660e5a415a8bd3f303b35abd42cc126432bc48a16c10c7","CHANGELOG.md":"983a8a4b94fd02e47bb5413393240e8e7da2f5cf39907a501efb46b68e83e103","Cargo.lock":"e42439098848e25a461c832877fd8a1a84bcf97eefd66db2302a4263065d5306","Cargo.toml":"950ec911574afbf038e970968c9bcd03df0fe56d78eabee157012e54d487cb29","Cargo.toml.orig":"8e6fa6e3eaf17939ca7b61fe0a5b013e356d7d895750a620b5a84f086123b9cd","LICENSE-APACHE":"a9040321c3712d8fd0b09cf52b17445de04a23a10165049ae187cd39e5c86be5","LICENSE-MIT":"2d1c57bff28344b9e698f51063bc8509799cc4c99a4e0cf2aa3f7e7c3e1f9a9d","README.md":"538b431a4ffd2fc83538a0d49d7da5b6d10a9c7d9319af9733ad8d69d12a5e1b","benches/mod.rs":"006f2402e8673e93177c22b6514165f5fd1e19b34c8771b0a23ef0385d50d492","src/alphabet.rs":"4a65137e34f9b8a45608554c5cc312f2f7780f6f4e2c8dfe90ffa341bd1a1b5f","src/alphabet/bcrypt.rs":"91ff8ea7356930ce6215211837748269266b81da9ba8c61b5fc714a2fc5cf611","src/alphabet/crypt.rs":"85f6d7f56db8192b7b79809d6766ad15a5da84912c2aaa95005f777a3ce1902b","src/alphabet/pbkdf2.rs":"73d7e4822405e4dfafbafafa8fbf3bce05adac03d24bbf05ee105b8115fa2a85","src/alphabet/shacrypt.rs":"d1e6ed75f1452f4e6b475325d1e0d1dc5b104c9cd85179b40a691dc2914099a3","src/alphabet/standard.rs":"f4a2ddc3ce658cba403da074ef2f0fd1fddb6b7f940cd39d209ec83d9d15b56a","src/alphabet/url.rs":"8558099e95df2cfc404bb6a64d077ad4b5c2a1de4d259062212213d9a6c82f64","src/decoder.rs":"be00bffbe72318bd46976fcfee35ab3354d3108d6f0f2d30079cbf509f39b87b","src/encoder.rs":"5bf0fcd3ba5c40e43bd420fc5fe6bbb0b36cf25aea04a4f29e05635fee927c5c","src/encoding.rs":"18828be1ed13a89e3cf723675b30addf8703be5c78583eb41bbea7b08de4b03f","src/errors.rs":"6c74f8cc350eba41488cb0a52514185cfc644e3ca480a0a8e09e38d0dd6cd713","src/lib.rs":"ebb6d9fe0d1199bb6aefaecb4e44524358c99d0db8d82e39b01a0b27f084b087","src/line_ending.rs":"fd8cdb28e947817cbb91e07147590f124aa88a9d3944484b42151aba67589be3","src/test_vectors.rs":"a194b755fab0649d6de702b9aaa220821f881806792a54c046dec5982fe128f7","tests/bcrypt.rs":"ed5ae2b87ad3d91200e34d9019e4b5df038510c4e77da7f35a650c0031ee131b","tests/common/mod.rs":"9ca32161bac1d8cb19bf62566229808e301308ec513299ba532152805abff01c","tests/crypt.rs":"f23e71a456668958832bcde51a28921422718274517a7e1c7d2eacdc848b35d7","tests/pbkdf2.rs":"a257fa0cf624c1ead5c86d953167e381b53c0bf1efb964c7c1660904ff09fad9","tests/proptests.proptest-regressions":"2a01eb7f3dc8f73490704630aa88362141fc260a9af42bf8896c131a4687076f","tests/proptests.rs":"0d1f172426a98267498fdcf4beccc7cb10e9977bd9121696791eec7e23ff0aed","tests/shacrypt.rs":"564c6e8ce2759d987b234642a4e910d82d0e892d974f2455805e93376506c51e","tests/standard.rs":"e72109a7a2aa7885ecdadffb6f51947e43e5e29609baef7e53eb1119dbfc2447","tests/url.rs":"076c4421e5f314da73f7a6da0c1c1657e30ead7938afd01cb510a77cd5d8f9e2"},"package":"2af50177e190e07a26ab74f8b1efbfe2ef87da2116221318cb1c2e82baf7de06"}

6
vendor/base64ct/.cargo_vcs_info.json vendored Normal file
View File

@@ -0,0 +1,6 @@
{
"git": {
"sha1": "9adf88fe3e6e0fb9f8cf20b54747aff67a3eca6e"
},
"path_in_vcs": "base64ct"
}

208
vendor/base64ct/CHANGELOG.md vendored Normal file
View File

@@ -0,0 +1,208 @@
# 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).
## 1.8.3 (2026-01-11)
### Added
- `Base64Pbkdf2` alphabet ([#2168])
[#2168]: https://github.com/RustCrypto/formats/pull/2168
## 1.8.2 (2026-01-03)
### Changed
- Deprecate `Base64Crypt` ([#2135])
[#2135]: https://github.com/RustCrypto/formats/pull/2135
## 1.8.1 (2025-12-06)
### Added
- Notes on `crypt(3)` alphabet variants ([#2073])
### Fixed
- Switch from `doc_auto_cfg` to `doc_cfg` ([#2072])
[#2072]: https://github.com/RustCrypto/formats/pull/2072
[#2073]: https://github.com/RustCrypto/formats/pull/2073
## 1.8.0 (2025-06-04)
### Changed
- Bump edition to 2024; MSRV 1.85 ([#1839])
[#1839]: https://github.com/RustCrypto/formats/pull/1839
## 1.7.3 (2025-03-13)
### Changed
- Don't fail with `InvalidLength` when reading nothing at end of data ([#1716]).
[#1716]: https://github.com/RustCrypto/formats/pull/1716
## 1.7.2 (2025-03-13)
### Changed
- Revert [#1387]: reject zero-length decode requests as it's a breaking change ([#1714])
[#1714]: https://github.com/RustCrypto/formats/pull/1714
## 1.7.1 (2025-03-10)
### Changed
- MSRV 1.81 - edition downgraded to 2021 from yanked 1.7.0 release ([#1702])
[#1702]: https://github.com/RustCrypto/formats/pull/1702
## 1.7.0 (2025-02-25) [YANKED]
### Added
- derive additional traits on alphabets ([#1578])
### Changed
- ~~MSRV 1.85 // Edition 2024 ([#1670])~~
- ~~reject zero-length decode requests ([#1387])~~
- use `core::error::Error` ([#1681])
[#1387]: https://github.com/RustCrypto/formats/pull/1387
[#1578]: https://github.com/RustCrypto/formats/pull/1578
[#1670]: https://github.com/RustCrypto/formats/pull/1670
[#1681]: https://github.com/RustCrypto/formats/pull/1681
## 1.6.0 (2023-02-26)
### Changed
- MSRV 1.60 ([#802])
- Lint improvements ([#824])
[#802]: https://github.com/RustCrypto/formats/pull/802
[#824]: https://github.com/RustCrypto/formats/pull/824
## 1.5.3 (2022-10-18)
### Added
- `Base64ShaCrypt` alphabet ([#742])
### Changed
- Use `RangeInclusive` for `DecodeStep` ([#713])
[#713]: https://github.com/RustCrypto/formats/pull/713
[#742]: https://github.com/RustCrypto/formats/pull/742
## 1.5.2 (2022-08-22)
### Fixed
- Return `Ok(0)` in `io::Read` impl to signal end of stream ([#704])
[#704]: https://github.com/RustCrypto/formats/pull/704
## 1.5.1 (2022-06-26)
### Fixed
- Last block validation ([#680])
[#680]: https://github.com/RustCrypto/formats/pull/680
## 1.5.0 (2022-03-29)
### Fixed
- Ensure checked arithmetic with `clippy::integer_arithmetic` lint ([#557])
- Prevent foreign impls of `Encoding` by bounding sealed `Variant` trait ([#562])
[#557]: https://github.com/RustCrypto/formats/pull/557
[#562]: https://github.com/RustCrypto/formats/pull/562
## 1.4.1 (2022-03-11)
### Changed
- Rename `Decoder::decoded_len` => `::remaining_len` ([#500])
[#500]: https://github.com/RustCrypto/formats/pull/500
## 1.4.0 (2022-03-10) [YANKED]
### Added
- Buffered `Encoder` type ([#366], [#455], [#457])
- `Decoder::decoded_len` method ([#403])
- Impl `std::io::Read` for `Decoder` ([#404])
- Bounds for `Encoding`/`Variant` ZSTs ([#405], [#408])
[#366]: https://github.com/RustCrypto/formats/pull/366
[#403]: https://github.com/RustCrypto/formats/pull/403
[#404]: https://github.com/RustCrypto/formats/pull/404
[#405]: https://github.com/RustCrypto/formats/pull/405
[#408]: https://github.com/RustCrypto/formats/pull/408
[#455]: https://github.com/RustCrypto/formats/pull/455
[#457]: https://github.com/RustCrypto/formats/pull/457
## 1.3.3 (2021-12-28)
### Fixed
- Potential infinite loop in `Decoder::decode` ([#305])
[#305]: https://github.com/RustCrypto/formats/pull/305
## 1.3.2 (2021-12-26) [YANKED]
### Fixed
- `Decoder` unpadding ([#299])
- Edge case when using `Decoder::new_wrapped` ([#300])
[#299]: https://github.com/RustCrypto/formats/pull/299
[#300]: https://github.com/RustCrypto/formats/pull/300
## 1.3.1 (2021-12-20) [YANKED]
### Added
- `Decoder::new_wrapped` with support for line-wrapped Base64 ([#292], [#293], [#294])
[#292]: https://github.com/RustCrypto/formats/pull/292
[#293]: https://github.com/RustCrypto/formats/pull/292
[#294]: https://github.com/RustCrypto/formats/pull/294
## 1.3.0 (2021-12-02) [YANKED]
### Added
- Stateful `Decoder` type ([#266])
[#266]: https://github.com/RustCrypto/formats/pull/266
## 1.2.0 (2021-11-03)
### Changed
- Rust 2021 edition upgrade; MSRV 1.56 ([#136])
### Fixed
- Benchmarks ([#135])
[#135]: https://github.com/RustCrypto/formats/pull/135
[#136]: https://github.com/RustCrypto/formats/pull/136
## 1.1.1 (2021-10-14)
### Changed
- Update `Util::Lookup` paper references ([#32])
[#32]: https://github.com/RustCrypto/formats/pull/32
## 1.1.0 (2021-09-14)
### Changed
- Moved to `formats` repo; MSRV 1.51+ ([#2])
[#2]: https://github.com/RustCrypto/formats/pull/2
## 1.0.1 (2021-08-14)
### Fixed
- Make `Encoding::decode` reject invalid padding
## 1.0.0 (2021-03-17)
### Changed
- Bump MSRV to 1.47+
### Fixed
- MSRV-dependent TODOs in implementation
## 0.2.1 (2021-03-07)
### Fixed
- MSRV docs
## 0.2.0 (2021-02-01)
### Changed
- Refactor with `Encoding` trait
- Internal refactoring
## 0.1.2 (2021-01-31)
### Added
- bcrypt encoding
- `crypt(3)` encoding
### Changed
- Internal refactoring
## 0.1.1 (2021-01-27)
- Minor code improvements
## 0.1.0 (2021-01-26)
- Initial release

212
vendor/base64ct/Cargo.lock generated vendored Normal file
View File

@@ -0,0 +1,212 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 4
[[package]]
name = "autocfg"
version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8"
[[package]]
name = "base64"
version = "0.22.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6"
[[package]]
name = "base64ct"
version = "1.8.3"
dependencies = [
"base64",
"proptest",
]
[[package]]
name = "bitflags"
version = "2.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "812e12b5285cc515a9c72a5c1d3b6d46a19dac5acfef5265968c166106e31dd3"
[[package]]
name = "cfg-if"
version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801"
[[package]]
name = "getrandom"
version = "0.3.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd"
dependencies = [
"cfg-if",
"libc",
"r-efi",
"wasip2",
]
[[package]]
name = "libc"
version = "0.2.179"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c5a2d376baa530d1238d133232d15e239abad80d05838b4b59354e5268af431f"
[[package]]
name = "num-traits"
version = "0.2.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841"
dependencies = [
"autocfg",
]
[[package]]
name = "ppv-lite86"
version = "0.2.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9"
dependencies = [
"zerocopy",
]
[[package]]
name = "proc-macro2"
version = "1.0.104"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9695f8df41bb4f3d222c95a67532365f569318332d03d5f3f67f37b20e6ebdf0"
dependencies = [
"unicode-ident",
]
[[package]]
name = "proptest"
version = "1.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bee689443a2bd0a16ab0348b52ee43e3b2d1b1f931c8aa5c9f8de4c86fbe8c40"
dependencies = [
"bitflags",
"num-traits",
"rand",
"rand_chacha",
"rand_xorshift",
"regex-syntax",
"unarray",
]
[[package]]
name = "quote"
version = "1.0.42"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a338cc41d27e6cc6dce6cefc13a0729dfbb81c262b1f519331575dd80ef3067f"
dependencies = [
"proc-macro2",
]
[[package]]
name = "r-efi"
version = "5.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f"
[[package]]
name = "rand"
version = "0.9.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6db2770f06117d490610c7488547d543617b21bfa07796d7a12f6f1bd53850d1"
dependencies = [
"rand_chacha",
"rand_core",
]
[[package]]
name = "rand_chacha"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb"
dependencies = [
"ppv-lite86",
"rand_core",
]
[[package]]
name = "rand_core"
version = "0.9.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "99d9a13982dcf210057a8a78572b2217b667c3beacbf3a0d8b454f6f82837d38"
dependencies = [
"getrandom",
]
[[package]]
name = "rand_xorshift"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "513962919efc330f829edb2535844d1b912b0fbe2ca165d613e4e8788bb05a5a"
dependencies = [
"rand_core",
]
[[package]]
name = "regex-syntax"
version = "0.8.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7a2d987857b319362043e95f5353c0535c1f58eec5336fdfcf626430af7def58"
[[package]]
name = "syn"
version = "2.0.113"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "678faa00651c9eb72dd2020cbdf275d92eccb2400d568e419efdd64838145cb4"
dependencies = [
"proc-macro2",
"quote",
"unicode-ident",
]
[[package]]
name = "unarray"
version = "0.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "eaea85b334db583fe3274d12b4cd1880032beab409c0d774be044d4480ab9a94"
[[package]]
name = "unicode-ident"
version = "1.0.22"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9312f7c4f6ff9069b165498234ce8be658059c6728633667c526e27dc2cf1df5"
[[package]]
name = "wasip2"
version = "1.0.1+wasi-0.2.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0562428422c63773dad2c345a1882263bbf4d65cf3f42e90921f787ef5ad58e7"
dependencies = [
"wit-bindgen",
]
[[package]]
name = "wit-bindgen"
version = "0.46.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f17a85883d4e6d00e8a97c586de764dabcc06133f7f1d55dce5cdc070ad7fe59"
[[package]]
name = "zerocopy"
version = "0.8.31"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fd74ec98b9250adb3ca554bdde269adf631549f51d8a8f8f0a10b50f1cb298c3"
dependencies = [
"zerocopy-derive",
]
[[package]]
name = "zerocopy-derive"
version = "0.8.31"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d8a8d209fdf45cf5138cbb5a506f6b52522a25afccc534d1475dad8e31105c6a"
dependencies = [
"proc-macro2",
"quote",
"syn",
]

101
vendor/base64ct/Cargo.toml vendored Normal file
View File

@@ -0,0 +1,101 @@
# 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 = "2024"
rust-version = "1.85"
name = "base64ct"
version = "1.8.3"
authors = ["RustCrypto Developers"]
build = false
autolib = false
autobins = false
autoexamples = false
autotests = false
autobenches = false
description = """
Pure Rust implementation of Base64 (RFC 4648) which avoids any usages of
data-dependent branches/LUTs and thereby provides portable "best effort"
constant-time operation and embedded-friendly no_std support
"""
homepage = "https://github.com/RustCrypto/formats/tree/master/base64ct"
documentation = "https://docs.rs/base64ct"
readme = "README.md"
keywords = [
"crypto",
"base64",
"pem",
"phc",
]
categories = [
"cryptography",
"encoding",
"no-std",
"parser-implementations",
]
license = "Apache-2.0 OR MIT"
repository = "https://github.com/RustCrypto/formats"
resolver = "2"
[package.metadata.docs.rs]
all-features = true
rustdoc-args = [
"--cfg",
"docsrs",
]
[features]
alloc = []
std = ["alloc"]
[lib]
name = "base64ct"
path = "src/lib.rs"
[[test]]
name = "bcrypt"
path = "tests/bcrypt.rs"
[[test]]
name = "crypt"
path = "tests/crypt.rs"
[[test]]
name = "pbkdf2"
path = "tests/pbkdf2.rs"
[[test]]
name = "proptests"
path = "tests/proptests.rs"
[[test]]
name = "shacrypt"
path = "tests/shacrypt.rs"
[[test]]
name = "standard"
path = "tests/standard.rs"
[[test]]
name = "url"
path = "tests/url.rs"
[[bench]]
name = "mod"
path = "benches/mod.rs"
[dev-dependencies.base64]
version = "0.22"
[dev-dependencies.proptest]
version = "1.6"
features = ["std"]
default-features = false

201
vendor/base64ct/LICENSE-APACHE vendored Normal file
View File

@@ -0,0 +1,201 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

26
vendor/base64ct/LICENSE-MIT vendored Normal file
View File

@@ -0,0 +1,26 @@
Copyright (c) 2014 Steve "Sc00bz" Thomas (steve at tobtu dot com)
Copyright (c) 2021-2025 The RustCrypto Project Developers
Permission is hereby granted, free of charge, to any
person obtaining a copy of this software and associated
documentation files (the "Software"), to deal in the
Software without restriction, including without
limitation the rights to use, copy, modify, merge,
publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software
is furnished to do so, subject to the following
conditions:
The above copyright notice and this permission notice
shall be included in all copies or substantial portions
of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.

87
vendor/base64ct/README.md vendored Normal file
View File

@@ -0,0 +1,87 @@
# [RustCrypto]: Constant-Time Base64
[![crate][crate-image]][crate-link]
[![Docs][docs-image]][docs-link]
[![Build Status][build-image]][build-link]
![Apache2/MIT licensed][license-image]
![Rust Version][rustc-image]
[![Project Chat][chat-image]][chat-link]
Pure Rust implementation of Base64 ([RFC 4648]).
Implements multiple Base64 alphabets without data-dependent branches or lookup
tables, thereby providing portable "best effort" constant-time operation.
Supports `no_std` environments and avoids heap allocations in the core API
(but also provides optional `alloc` support for convenience).
[Documentation][docs-link]
## About
This crate implements several Base64 alphabets in constant-time for sidechannel
resistance, aimed at purposes like encoding/decoding the "PEM" format used to
store things like cryptographic private keys (i.e. in the [`pem-rfc7468`] crate).
The paper [Util::Lookup: Exploiting key decoding in cryptographic libraries][Util::Lookup]
demonstrates how the leakage from non-constant-time Base64 parsers can be used
to practically extract RSA private keys from SGX enclaves.
The padded variants require (`=`) padding. Unpadded variants expressly
reject such padding.
Whitespace is expressly disallowed, with the exception of the
[`Decoder::new_wrapped`] and [`Encoder::new_wrapped`] modes which provide
fixed-width line wrapping.
## Supported Base64 variants
- Standard Base64: `[A-Z]`, `[a-z]`, `[0-9]`, `+`, `/`
- URL-safe Base64: `[A-Z]`, `[a-z]`, `[0-9]`, `-`, `_`
- bcrypt Base64: `.`, `/`, `[A-Z]`, `[a-z]`, `[0-9]`
- `crypt(3)` Base64: `.`, `-`, `[0-9]`, `[A-Z]`, `[a-z]`
- PBKDF2 Base64: `[A-Z]`, `[a-z]`, `[0-9]`, `.`, `/`
## Minimum Supported Rust Version (MSRV) Policy
MSRV increases are not considered breaking changes and can happen in patch releases.
The crate MSRV accounts for all supported targets and crate feature combinations, excluding
explicitly unstable features.
## License
Licensed under either of:
* [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0)
* [MIT license](http://opensource.org/licenses/MIT)
at your option.
### Contribution
Unless you explicitly state otherwise, any contribution intentionally submitted
for inclusion in the work by you, as defined in the Apache-2.0 license, shall be
dual licensed as above, without any additional terms or conditions.
[//]: # (badges)
[crate-image]: https://img.shields.io/crates/v/base64ct?logo=rust
[crate-link]: https://crates.io/crates/base64ct
[docs-image]: https://docs.rs/base64ct/badge.svg
[docs-link]: https://docs.rs/base64ct/
[build-image]: https://github.com/RustCrypto/formats/actions/workflows/base64ct.yml/badge.svg
[build-link]: https://github.com/RustCrypto/formats/actions/workflows/base64ct.yml
[license-image]: https://img.shields.io/badge/license-Apache2.0/MIT-blue.svg
[rustc-image]: https://img.shields.io/badge/rustc-1.85+-blue.svg
[chat-image]: https://img.shields.io/badge/zulip-join_chat-blue.svg
[chat-link]: https://rustcrypto.zulipchat.com/#narrow/stream/300570-formats
[//]: # (links)
[RustCrypto]: https://github.com/rustcrypto
[RFC 4648]: https://tools.ietf.org/html/rfc4648
[`pem-rfc7468`]: https://github.com/RustCrypto/formats/tree/master/pem-rfc7468
[Util::Lookup]: https://arxiv.org/pdf/2108.04600.pdf
[`Decoder::new_wrapped`]: https://docs.rs/base64ct/latest/base64ct/struct.Decoder.html#method.new_wrapped
[`Encoder::new_wrapped`]: https://docs.rs/base64ct/latest/base64ct/struct.Encoder.html#method.new_wrapped

62
vendor/base64ct/benches/mod.rs vendored Normal file
View File

@@ -0,0 +1,62 @@
//! `base64ct` benchmarks
#![feature(test)]
extern crate test;
use base64ct::{Base64Unpadded, Encoding};
use test::Bencher;
const B64_LEN: usize = 100_000;
const RAW_LEN: usize = (3 * B64_LEN) / 4;
#[inline(never)]
fn get_raw_data() -> Vec<u8> {
(0..RAW_LEN).map(|i| i as u8).collect()
}
#[inline(never)]
fn get_b64_data() -> String {
(0..B64_LEN)
.map(|i| match (i % 64) as u8 {
v @ 0..=25 => (v + b'A') as char,
v @ 26..=51 => (v - 26 + b'a') as char,
v @ 52..=61 => (v - 52 + b'0') as char,
62 => '+',
_ => '/',
})
.collect()
}
#[bench]
fn decode_bench(b: &mut Bencher) {
let b64_data = get_b64_data();
let mut buf = get_raw_data();
b.iter(|| {
let out = Base64Unpadded::decode(test::black_box(&b64_data), &mut buf).unwrap();
test::black_box(out);
});
b.bytes = RAW_LEN as u64;
}
#[bench]
fn decode_in_place_bench(b: &mut Bencher) {
let b64_data = get_b64_data().into_bytes();
let mut buf = b64_data.clone();
b.iter(|| {
buf.copy_from_slice(&b64_data[..]);
let out = Base64Unpadded::decode_in_place(&mut buf);
let _ = test::black_box(out);
});
b.bytes = RAW_LEN as u64;
}
#[bench]
fn encode_bench(b: &mut Bencher) {
let mut buf = get_b64_data().into_bytes();
let raw_data = get_raw_data();
b.iter(|| {
let out = Base64Unpadded::encode(test::black_box(&raw_data), &mut buf).unwrap();
test::black_box(out);
});
b.bytes = RAW_LEN as u64;
}

125
vendor/base64ct/src/alphabet.rs vendored Normal file
View File

@@ -0,0 +1,125 @@
//! Base64 alphabets.
// TODO(tarcieri): explicitly checked/wrapped arithmetic
#![allow(clippy::arithmetic_side_effects)]
use core::{fmt::Debug, ops::RangeInclusive};
pub mod bcrypt;
pub mod crypt;
pub mod pbkdf2;
pub mod shacrypt;
pub mod standard;
pub mod url;
/// Core encoder/decoder functions for a particular Base64 alphabet.
pub trait Alphabet: 'static + Copy + Debug + Eq + Send + Sized + Sync {
/// First character in this Base64 alphabet.
const BASE: u8;
/// Decoder passes
const DECODER: &'static [DecodeStep];
/// Encoder passes
const ENCODER: &'static [EncodeStep];
/// Is this encoding padded?
const PADDED: bool;
/// Unpadded equivalent of this alphabet.
///
/// For alphabets that are unpadded to begin with, this should be `Self`.
type Unpadded: Alphabet;
/// Decode 3 bytes of a Base64 message.
#[inline(always)]
fn decode_3bytes(src: &[u8], dst: &mut [u8]) -> i16 {
debug_assert_eq!(src.len(), 4);
debug_assert!(dst.len() >= 3, "dst too short: {}", dst.len());
let c0 = Self::decode_6bits(src[0]);
let c1 = Self::decode_6bits(src[1]);
let c2 = Self::decode_6bits(src[2]);
let c3 = Self::decode_6bits(src[3]);
dst[0] = ((c0 << 2) | (c1 >> 4)) as u8;
dst[1] = ((c1 << 4) | (c2 >> 2)) as u8;
dst[2] = ((c2 << 6) | c3) as u8;
((c0 | c1 | c2 | c3) >> 8) & 1
}
/// Decode 6-bits of a Base64 message.
fn decode_6bits(src: u8) -> i16 {
let mut ret: i16 = -1;
for step in Self::DECODER {
ret += match step {
DecodeStep::Range(range, offset) => {
// Compute exclusive range from inclusive one
let start = *range.start() as i16 - 1;
let end = *range.end() as i16 + 1;
(((start - src as i16) & (src as i16 - end)) >> 8) & (src as i16 + *offset)
}
DecodeStep::Eq(value, offset) => {
let start = *value as i16 - 1;
let end = *value as i16 + 1;
(((start - src as i16) & (src as i16 - end)) >> 8) & *offset
}
};
}
ret
}
/// Encode 3-bytes of a Base64 message.
#[inline(always)]
fn encode_3bytes(src: &[u8], dst: &mut [u8]) {
debug_assert_eq!(src.len(), 3);
debug_assert!(dst.len() >= 4, "dst too short: {}", dst.len());
let b0 = src[0] as i16;
let b1 = src[1] as i16;
let b2 = src[2] as i16;
dst[0] = Self::encode_6bits(b0 >> 2);
dst[1] = Self::encode_6bits(((b0 << 4) | (b1 >> 4)) & 63);
dst[2] = Self::encode_6bits(((b1 << 2) | (b2 >> 6)) & 63);
dst[3] = Self::encode_6bits(b2 & 63);
}
/// Encode 6-bits of a Base64 message.
#[inline(always)]
fn encode_6bits(src: i16) -> u8 {
let mut diff = src + Self::BASE as i16;
for &step in Self::ENCODER {
diff += match step {
EncodeStep::Apply(threshold, offset) => ((threshold as i16 - diff) >> 8) & offset,
EncodeStep::Diff(threshold, offset) => ((threshold as i16 - src) >> 8) & offset,
};
}
diff as u8
}
}
/// Constant-time decoder step.
#[derive(Debug)]
pub enum DecodeStep {
/// Match the given range, offsetting the input on match.
Range(RangeInclusive<u8>, i16),
/// Match the given value, returning the associated offset on match.
Eq(u8, i16),
}
/// Constant-time encoder step.
#[derive(Copy, Clone, Debug)]
pub enum EncodeStep {
/// Apply the given offset to the cumulative result on match.
Apply(u8, i16),
/// Compute a difference using the given offset on match.
Diff(u8, i16),
}

33
vendor/base64ct/src/alphabet/bcrypt.rs vendored Normal file
View File

@@ -0,0 +1,33 @@
//! bcrypt Base64 encoding.
use super::{Alphabet, DecodeStep, EncodeStep};
/// bcrypt Base64 encoding.
///
/// ```text
/// ./ [A-Z] [a-z] [0-9]
/// 0x2e-0x2f, 0x41-0x5a, 0x61-0x7a, 0x30-0x39
/// ```
#[derive(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub struct Base64Bcrypt;
impl Alphabet for Base64Bcrypt {
const BASE: u8 = b'.';
const DECODER: &'static [DecodeStep] = &[
DecodeStep::Range(b'.'..=b'/', -45),
DecodeStep::Range(b'A'..=b'Z', -62),
DecodeStep::Range(b'a'..=b'z', -68),
DecodeStep::Range(b'0'..=b'9', 7),
];
const ENCODER: &'static [EncodeStep] = &[
EncodeStep::Apply(b'/', 17),
EncodeStep::Apply(b'Z', 6),
EncodeStep::Apply(b'z', -75),
];
const PADDED: bool = false;
type Unpadded = Self;
}

40
vendor/base64ct/src/alphabet/crypt.rs vendored Normal file
View File

@@ -0,0 +1,40 @@
//! `crypt(3)` Base64 encoding.
#![allow(deprecated)]
use super::{Alphabet, DecodeStep, EncodeStep};
/// DEPRECATED: non-standard big endian variant of the `crypt(3)` Base64 encoding.
///
/// ```text
/// [.-9] [A-Z] [a-z]
/// 0x2e-0x39, 0x41-0x5a, 0x61-0x7a
/// ```
///
/// <div class="warning">
/// This encodes using a big endian variant of Base64. Most modern algorithms which can be
/// used via `crypt(3)` use the [`Base64ShaCrypt`][`crate::Base64ShaCrypt`] encoding.
/// </div>
#[deprecated(
since = "1.8.2",
note = "non-standard encoding. Use Base64ShaCrypt for all crypt(3) algorithms"
)]
#[derive(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub struct Base64Crypt;
impl Alphabet for Base64Crypt {
const BASE: u8 = b'.';
const DECODER: &'static [DecodeStep] = &[
DecodeStep::Range(b'.'..=b'9', -45),
DecodeStep::Range(b'A'..=b'Z', -52),
DecodeStep::Range(b'a'..=b'z', -58),
];
const ENCODER: &'static [EncodeStep] =
&[EncodeStep::Apply(b'9', 7), EncodeStep::Apply(b'Z', 6)];
const PADDED: bool = false;
type Unpadded = Self;
}

33
vendor/base64ct/src/alphabet/pbkdf2.rs vendored Normal file
View File

@@ -0,0 +1,33 @@
use crate::alphabet::{Alphabet, DecodeStep, EncodeStep};
/// PBKDF2 Base64: variant of unpadded standard Base64 with `.` instead of `+`.
///
/// ```text
/// [A-Z] [a-z] [0-9] . /
/// 0x41-0x5a, 0x61-0x7a, 0x30-0x39, 0x2e, 0x2f
/// ```
#[derive(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub struct Base64Pbkdf2;
impl Alphabet for Base64Pbkdf2 {
const BASE: u8 = b'A';
const DECODER: &'static [DecodeStep] = DECODER;
const ENCODER: &'static [EncodeStep] = ENCODER;
const PADDED: bool = false;
type Unpadded = Self;
}
const DECODER: &[DecodeStep] = &[
DecodeStep::Range(b'A'..=b'Z', -64),
DecodeStep::Range(b'a'..=b'z', -70),
DecodeStep::Range(b'0'..=b'9', 5),
DecodeStep::Eq(b'.', 63),
DecodeStep::Eq(b'/', 64),
];
const ENCODER: &[EncodeStep] = &[
EncodeStep::Diff(25, 6),
EncodeStep::Diff(51, -75),
EncodeStep::Diff(61, -(b'.' as i16 - 0x22)),
EncodeStep::Diff(62, b'/' as i16 - b'.' as i16 - 1),
];

View File

@@ -0,0 +1,69 @@
//! `crypt(3)` Base64 encoding for sha* family.
use super::{Alphabet, DecodeStep, EncodeStep};
/// `crypt(3)` Base64 encoding.
///
/// This is the standard Base64 encoding used by password hashes for the following schemes:
/// - MD5-Crypt
/// - scrypt
/// - SHA1-Crypt
/// - SHA256-Crypt
/// - SHA512-Crypt
/// - yescrypt
///
/// ```text
/// [.-9] [A-Z] [a-z]
/// 0x2e-0x39, 0x41-0x5a, 0x61-0x7a
/// ```
#[derive(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub struct Base64ShaCrypt;
impl Alphabet for Base64ShaCrypt {
const BASE: u8 = b'.';
const DECODER: &'static [DecodeStep] = &[
DecodeStep::Range(b'.'..=b'9', -45),
DecodeStep::Range(b'A'..=b'Z', -52),
DecodeStep::Range(b'a'..=b'z', -58),
];
const ENCODER: &'static [EncodeStep] =
&[EncodeStep::Apply(b'9', 7), EncodeStep::Apply(b'Z', 6)];
const PADDED: bool = false;
type Unpadded = Self;
#[inline(always)]
fn decode_3bytes(src: &[u8], dst: &mut [u8]) -> i16 {
debug_assert_eq!(src.len(), 4);
debug_assert!(dst.len() >= 3, "dst too short: {}", dst.len());
let c0 = Self::decode_6bits(src[0]);
let c1 = Self::decode_6bits(src[1]);
let c2 = Self::decode_6bits(src[2]);
let c3 = Self::decode_6bits(src[3]);
dst[0] = (c0 | ((c1 & 0x3) << 6)) as u8;
dst[1] = ((c1 >> 2) | ((c2 & 0xF) << 4)) as u8;
dst[2] = ((c2 >> 4) | (c3 << 2)) as u8;
((c0 | c1 | c2 | c3) >> 8) & 1
}
#[inline(always)]
fn encode_3bytes(src: &[u8], dst: &mut [u8]) {
debug_assert_eq!(src.len(), 3);
debug_assert!(dst.len() >= 4, "dst too short: {}", dst.len());
let b0 = src[0] as i16;
let b1 = src[1] as i16;
let b2 = src[2] as i16;
dst[0] = Self::encode_6bits(b0 & 63);
dst[1] = Self::encode_6bits(((b1 << 2) | (b0 >> 6)) & 63);
dst[2] = Self::encode_6bits(((b2 << 4) | (b1 >> 4)) & 63);
dst[3] = Self::encode_6bits(b2 >> 2);
}
}

View File

@@ -0,0 +1,54 @@
//! Standard Base64 encoding.
use super::{Alphabet, DecodeStep, EncodeStep};
/// Standard Base64 encoding with `=` padding.
///
/// ```text
/// [A-Z] [a-z] [0-9] + /
/// 0x41-0x5a, 0x61-0x7a, 0x30-0x39, 0x2b, 0x2f
/// ```
#[derive(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub struct Base64;
impl Alphabet for Base64 {
const BASE: u8 = b'A';
const DECODER: &'static [DecodeStep] = DECODER;
const ENCODER: &'static [EncodeStep] = ENCODER;
const PADDED: bool = true;
type Unpadded = Base64Unpadded;
}
/// Standard Base64 encoding *without* padding.
///
/// ```text
/// [A-Z] [a-z] [0-9] + /
/// 0x41-0x5a, 0x61-0x7a, 0x30-0x39, 0x2b, 0x2f
/// ```
#[derive(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub struct Base64Unpadded;
impl Alphabet for Base64Unpadded {
const BASE: u8 = b'A';
const DECODER: &'static [DecodeStep] = DECODER;
const ENCODER: &'static [EncodeStep] = ENCODER;
const PADDED: bool = false;
type Unpadded = Self;
}
/// Standard Base64 decoder
const DECODER: &[DecodeStep] = &[
DecodeStep::Range(b'A'..=b'Z', -64),
DecodeStep::Range(b'a'..=b'z', -70),
DecodeStep::Range(b'0'..=b'9', 5),
DecodeStep::Eq(b'+', 63),
DecodeStep::Eq(b'/', 64),
];
/// Standard Base64 encoder
const ENCODER: &[EncodeStep] = &[
EncodeStep::Diff(25, 6),
EncodeStep::Diff(51, -75),
EncodeStep::Diff(61, -(b'+' as i16 - 0x1c)),
EncodeStep::Diff(62, b'/' as i16 - b'+' as i16 - 1),
];

54
vendor/base64ct/src/alphabet/url.rs vendored Normal file
View File

@@ -0,0 +1,54 @@
//! URL-safe Base64 encoding.
use super::{Alphabet, DecodeStep, EncodeStep};
/// URL-safe Base64 encoding with `=` padding.
///
/// ```text
/// [A-Z] [a-z] [0-9] - _
/// 0x41-0x5a, 0x61-0x7a, 0x30-0x39, 0x2d, 0x5f
/// ```
#[derive(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub struct Base64Url;
impl Alphabet for Base64Url {
const BASE: u8 = b'A';
const DECODER: &'static [DecodeStep] = DECODER;
const ENCODER: &'static [EncodeStep] = ENCODER;
const PADDED: bool = true;
type Unpadded = Base64UrlUnpadded;
}
/// URL-safe Base64 encoding *without* padding.
///
/// ```text
/// [A-Z] [a-z] [0-9] - _
/// 0x41-0x5a, 0x61-0x7a, 0x30-0x39, 0x2d, 0x5f
/// ```
#[derive(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub struct Base64UrlUnpadded;
impl Alphabet for Base64UrlUnpadded {
const BASE: u8 = b'A';
const DECODER: &'static [DecodeStep] = DECODER;
const ENCODER: &'static [EncodeStep] = ENCODER;
const PADDED: bool = false;
type Unpadded = Self;
}
/// URL-safe Base64 decoder
const DECODER: &[DecodeStep] = &[
DecodeStep::Range(b'A'..=b'Z', -64),
DecodeStep::Range(b'a'..=b'z', -70),
DecodeStep::Range(b'0'..=b'9', 5),
DecodeStep::Eq(b'-', 63),
DecodeStep::Eq(b'_', 64),
];
/// URL-safe Base64 encoder
const ENCODER: &[EncodeStep] = &[
EncodeStep::Diff(25, 6),
EncodeStep::Diff(51, -75),
EncodeStep::Diff(61, -(b'-' as i16 - 0x20)),
EncodeStep::Diff(62, b'_' as i16 - b'-' as i16 - 1),
];

635
vendor/base64ct/src/decoder.rs vendored Normal file
View File

@@ -0,0 +1,635 @@
//! Buffered Base64 decoder.
use crate::{
Encoding,
Error::{self, InvalidLength},
MIN_LINE_WIDTH, encoding,
line_ending::{CHAR_CR, CHAR_LF},
};
use core::{cmp, marker::PhantomData};
#[cfg(feature = "alloc")]
use {alloc::vec::Vec, core::iter};
#[cfg(feature = "std")]
use std::io;
#[cfg(doc)]
use crate::{Base64, Base64Unpadded};
/// Stateful Base64 decoder with support for buffered, incremental decoding.
///
/// The `E` type parameter can be any type which impls [`Encoding`] such as
/// [`Base64`] or [`Base64Unpadded`].
#[derive(Clone)]
pub struct Decoder<'i, E: Encoding> {
/// Current line being processed.
line: Line<'i>,
/// Base64 input data reader.
line_reader: LineReader<'i>,
/// Length of the remaining data after Base64 decoding.
remaining_len: usize,
/// Block buffer used for non-block-aligned data.
block_buffer: BlockBuffer,
/// Phantom parameter for the Base64 encoding in use.
encoding: PhantomData<E>,
}
impl<'i, E: Encoding> Decoder<'i, E> {
/// Create a new decoder for a byte slice containing contiguous
/// (non-newline-delimited) Base64-encoded data.
///
/// # Returns
/// - `Ok(decoder)` on success.
/// - `Err(Error::InvalidLength)` if the input buffer is empty.
pub fn new(input: &'i [u8]) -> Result<Self, Error> {
let line_reader = LineReader::new_unwrapped(input)?;
let remaining_len = line_reader.decoded_len::<E>()?;
Ok(Self {
line: Line::default(),
line_reader,
remaining_len,
block_buffer: BlockBuffer::default(),
encoding: PhantomData,
})
}
/// Create a new decoder for a byte slice containing Base64 which
/// line wraps at the given line length.
///
/// Trailing newlines are not supported and must be removed in advance.
///
/// Newlines are handled according to what are roughly [RFC7468] conventions:
///
/// ```text
/// [parsers] MUST handle different newline conventions
/// ```
///
/// RFC7468 allows any of the following as newlines, and allows a mixture
/// of different types of newlines:
///
/// ```text
/// eol = CRLF / CR / LF
/// ```
///
/// # Returns
/// - `Ok(decoder)` on success.
/// - `Err(Error::InvalidLength)` if the input buffer is empty or the line
/// width is zero.
///
/// [RFC7468]: https://datatracker.ietf.org/doc/html/rfc7468
pub fn new_wrapped(input: &'i [u8], line_width: usize) -> Result<Self, Error> {
let line_reader = LineReader::new_wrapped(input, line_width)?;
let remaining_len = line_reader.decoded_len::<E>()?;
Ok(Self {
line: Line::default(),
line_reader,
remaining_len,
block_buffer: BlockBuffer::default(),
encoding: PhantomData,
})
}
/// Fill the provided buffer with data decoded from Base64.
///
/// Enough Base64 input data must remain to fill the entire buffer.
///
/// # Returns
/// - `Ok(bytes)` if the expected amount of data was read
/// - `Err(Error::InvalidLength)` if the exact amount of data couldn't be read
pub fn decode<'o>(&mut self, out: &'o mut [u8]) -> Result<&'o [u8], Error> {
if self.is_finished() && !out.is_empty() {
return Err(InvalidLength);
}
let mut out_pos = 0;
while out_pos < out.len() {
// If there's data in the block buffer, use it
if !self.block_buffer.is_empty() {
let out_rem = out.len().checked_sub(out_pos).ok_or(InvalidLength)?;
let bytes = self.block_buffer.take(out_rem)?;
out[out_pos..][..bytes.len()].copy_from_slice(bytes);
out_pos = out_pos.checked_add(bytes.len()).ok_or(InvalidLength)?;
}
// Advance the line reader if necessary
if self.line.is_empty() && !self.line_reader.is_empty() {
self.advance_line()?;
}
// Attempt to decode a stride of block-aligned data
let in_blocks = self.line.len() / 4;
let out_rem = out.len().checked_sub(out_pos).ok_or(InvalidLength)?;
let out_blocks = out_rem / 3;
let blocks = cmp::min(in_blocks, out_blocks);
let in_aligned = self.line.take(blocks.checked_mul(4).ok_or(InvalidLength)?);
if !in_aligned.is_empty() {
let out_buf = &mut out[out_pos..][..blocks.checked_mul(3).ok_or(InvalidLength)?];
let decoded_len = self.perform_decode(in_aligned, out_buf)?.len();
out_pos = out_pos.checked_add(decoded_len).ok_or(InvalidLength)?;
}
if out_pos < out.len() {
if self.is_finished() {
// If we're out of input then we've been requested to decode
// more data than is actually available.
return Err(InvalidLength);
} else {
// If we still have data available but haven't completely
// filled the output slice, we're in a situation where
// either the input or output isn't block-aligned, so fill
// the internal block buffer.
self.fill_block_buffer()?;
}
}
}
self.remaining_len = self
.remaining_len
.checked_sub(out.len())
.ok_or(InvalidLength)?;
Ok(out)
}
/// Decode all remaining Base64 data, placing the result into `buf`.
///
/// If successful, this function will return the total number of bytes
/// decoded into `buf`.
#[cfg(feature = "alloc")]
pub fn decode_to_end<'o>(&mut self, buf: &'o mut Vec<u8>) -> Result<&'o [u8], Error> {
let start_len = buf.len();
let remaining_len = self.remaining_len();
let total_len = start_len.checked_add(remaining_len).ok_or(InvalidLength)?;
if total_len > buf.capacity() {
buf.reserve(total_len.checked_sub(buf.capacity()).ok_or(InvalidLength)?);
}
// Append `decoded_len` zeroes to the vector
buf.extend(iter::repeat_n(0, remaining_len));
self.decode(&mut buf[start_len..])?;
Ok(&buf[start_len..])
}
/// Get the length of the remaining data after Base64 decoding.
///
/// Decreases every time data is decoded.
pub fn remaining_len(&self) -> usize {
self.remaining_len
}
/// Has all of the input data been decoded?
pub fn is_finished(&self) -> bool {
self.line.is_empty() && self.line_reader.is_empty() && self.block_buffer.is_empty()
}
/// Fill the block buffer with data.
fn fill_block_buffer(&mut self) -> Result<(), Error> {
let mut buf = [0u8; BlockBuffer::SIZE];
let decoded = if self.line.len() < 4 && !self.line_reader.is_empty() {
// Handle input block which is split across lines
let mut tmp = [0u8; 4];
// Copy remaining data in the line into tmp
let line_end = self.line.take(4);
tmp[..line_end.len()].copy_from_slice(line_end);
// Advance the line and attempt to fill tmp
self.advance_line()?;
let len = 4usize.checked_sub(line_end.len()).ok_or(InvalidLength)?;
let line_begin = self.line.take(len);
tmp[line_end.len()..][..line_begin.len()].copy_from_slice(line_begin);
let tmp_len = line_begin
.len()
.checked_add(line_end.len())
.ok_or(InvalidLength)?;
self.perform_decode(&tmp[..tmp_len], &mut buf)
} else {
let block = self.line.take(4);
self.perform_decode(block, &mut buf)
}?;
self.block_buffer.fill(decoded)
}
/// Advance the internal buffer to the next line.
fn advance_line(&mut self) -> Result<(), Error> {
debug_assert!(self.line.is_empty(), "expected line buffer to be empty");
if let Some(line) = self.line_reader.next().transpose()? {
self.line = line;
Ok(())
} else {
Err(InvalidLength)
}
}
/// Perform Base64 decoding operation.
fn perform_decode<'o>(&self, src: &[u8], dst: &'o mut [u8]) -> Result<&'o [u8], Error> {
if self.is_finished() {
E::decode(src, dst)
} else {
E::Unpadded::decode(src, dst)
}
}
}
#[cfg(feature = "std")]
impl<E: Encoding> io::Read for Decoder<'_, E> {
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
if self.is_finished() {
return Ok(0);
}
let slice = match buf.get_mut(..self.remaining_len()) {
Some(bytes) => bytes,
None => buf,
};
self.decode(slice)?;
Ok(slice.len())
}
fn read_to_end(&mut self, buf: &mut Vec<u8>) -> io::Result<usize> {
if self.is_finished() {
return Ok(0);
}
Ok(self.decode_to_end(buf)?.len())
}
fn read_exact(&mut self, buf: &mut [u8]) -> io::Result<()> {
self.decode(buf)?;
Ok(())
}
}
/// Base64 decode buffer for a 1-block input.
///
/// This handles a partially decoded block of data, i.e. data which has been
/// decoded but not read.
#[derive(Clone, Default, Debug)]
struct BlockBuffer {
/// 3 decoded bytes from a 4-byte Base64-encoded input.
decoded: [u8; Self::SIZE],
/// Length of the buffer.
length: usize,
/// Position within the buffer.
position: usize,
}
impl BlockBuffer {
/// Size of the buffer in bytes.
const SIZE: usize = 3;
/// Fill the buffer by decoding up to 3 bytes of decoded Base64 input.
fn fill(&mut self, decoded_input: &[u8]) -> Result<(), Error> {
debug_assert!(self.is_empty());
if decoded_input.len() > Self::SIZE {
return Err(InvalidLength);
}
self.position = 0;
self.length = decoded_input.len();
self.decoded[..decoded_input.len()].copy_from_slice(decoded_input);
Ok(())
}
/// Take a specified number of bytes from the buffer.
///
/// Returns as many bytes as possible, or an empty slice if the buffer has
/// already been read to completion.
fn take(&mut self, mut nbytes: usize) -> Result<&[u8], Error> {
debug_assert!(self.position <= self.length);
let start_pos = self.position;
let remaining_len = self.length.checked_sub(start_pos).ok_or(InvalidLength)?;
if nbytes > remaining_len {
nbytes = remaining_len;
}
self.position = self.position.checked_add(nbytes).ok_or(InvalidLength)?;
Ok(&self.decoded[start_pos..][..nbytes])
}
/// Have all of the bytes in this buffer been consumed?
fn is_empty(&self) -> bool {
self.position == self.length
}
}
/// A single line of linewrapped data, providing a read buffer.
#[derive(Clone, Debug)]
pub struct Line<'i> {
/// Remaining data in the line
remaining: &'i [u8],
}
impl Default for Line<'_> {
fn default() -> Self {
Self::new(&[])
}
}
impl<'i> Line<'i> {
/// Create a new line which wraps the given input data.
fn new(bytes: &'i [u8]) -> Self {
Self { remaining: bytes }
}
/// Take up to `nbytes` from this line buffer.
fn take(&mut self, nbytes: usize) -> &'i [u8] {
let (bytes, rest) = if nbytes < self.remaining.len() {
self.remaining.split_at(nbytes)
} else {
(self.remaining, [].as_ref())
};
self.remaining = rest;
bytes
}
/// Slice off a tail of a given length.
fn slice_tail(&self, nbytes: usize) -> Result<&'i [u8], Error> {
let offset = self.len().checked_sub(nbytes).ok_or(InvalidLength)?;
self.remaining.get(offset..).ok_or(InvalidLength)
}
/// Get the number of bytes remaining in this line.
fn len(&self) -> usize {
self.remaining.len()
}
/// Is the buffer for this line empty?
fn is_empty(&self) -> bool {
self.len() == 0
}
/// Trim the newline off the end of this line.
fn trim_end(&self) -> Self {
Line::new(match self.remaining {
[line @ .., CHAR_CR, CHAR_LF] => line,
[line @ .., CHAR_CR] => line,
[line @ .., CHAR_LF] => line,
line => line,
})
}
}
/// Iterator over multi-line Base64 input.
#[derive(Clone)]
struct LineReader<'i> {
/// Remaining linewrapped data to be processed.
remaining: &'i [u8],
/// Line width.
line_width: Option<usize>,
}
impl<'i> LineReader<'i> {
/// Create a new reader which operates over continugous unwrapped data.
fn new_unwrapped(bytes: &'i [u8]) -> Result<Self, Error> {
if bytes.is_empty() {
Err(InvalidLength)
} else {
Ok(Self {
remaining: bytes,
line_width: None,
})
}
}
/// Create a new reader which operates over linewrapped data.
fn new_wrapped(bytes: &'i [u8], line_width: usize) -> Result<Self, Error> {
if line_width < MIN_LINE_WIDTH {
return Err(InvalidLength);
}
let mut reader = Self::new_unwrapped(bytes)?;
reader.line_width = Some(line_width);
Ok(reader)
}
/// Is this line reader empty?
fn is_empty(&self) -> bool {
self.remaining.is_empty()
}
/// Get the total length of the data decoded from this line reader.
fn decoded_len<E: Encoding>(&self) -> Result<usize, Error> {
let mut buffer = [0u8; 4];
let mut lines = self.clone();
let mut line = match lines.next().transpose()? {
Some(l) => l,
None => return Ok(0),
};
let mut base64_len = 0usize;
loop {
base64_len = base64_len.checked_add(line.len()).ok_or(InvalidLength)?;
match lines.next().transpose()? {
Some(l) => {
// Store the end of the line in the buffer so we can
// reassemble the last block to determine the real length
buffer.copy_from_slice(line.slice_tail(4)?);
line = l
}
// To compute an exact decoded length we need to decode the
// last Base64 block and get the decoded length.
//
// This is what the somewhat complex code below is doing.
None => {
// Compute number of bytes in the last block (may be unpadded)
let base64_last_block_len = match base64_len % 4 {
0 => 4,
n => n,
};
// Compute decoded length without the last block
let decoded_len = encoding::decoded_len(
base64_len
.checked_sub(base64_last_block_len)
.ok_or(InvalidLength)?,
);
// Compute the decoded length of the last block
let mut out = [0u8; 3];
let last_block_len = if line.len() < base64_last_block_len {
let buffered_part_len = base64_last_block_len
.checked_sub(line.len())
.ok_or(InvalidLength)?;
let offset = 4usize.checked_sub(buffered_part_len).ok_or(InvalidLength)?;
for i in 0..buffered_part_len {
buffer[i] = buffer[offset.checked_add(i).ok_or(InvalidLength)?];
}
buffer[buffered_part_len..][..line.len()].copy_from_slice(line.remaining);
let buffer_len = buffered_part_len
.checked_add(line.len())
.ok_or(InvalidLength)?;
E::decode(&buffer[..buffer_len], &mut out)?.len()
} else {
let last_block = line.slice_tail(base64_last_block_len)?;
E::decode(last_block, &mut out)?.len()
};
return decoded_len.checked_add(last_block_len).ok_or(InvalidLength);
}
}
}
}
}
impl<'i> Iterator for LineReader<'i> {
type Item = Result<Line<'i>, Error>;
fn next(&mut self) -> Option<Result<Line<'i>, Error>> {
if let Some(line_width) = self.line_width {
let rest = match self.remaining.get(line_width..) {
None | Some([]) => {
if self.remaining.is_empty() {
return None;
} else {
let line = Line::new(self.remaining).trim_end();
self.remaining = &[];
return Some(Ok(line));
}
}
Some([CHAR_CR, CHAR_LF, rest @ ..]) => rest,
Some([CHAR_CR, rest @ ..]) => rest,
Some([CHAR_LF, rest @ ..]) => rest,
_ => {
// Expected a leading newline
return Some(Err(Error::InvalidEncoding));
}
};
let line = Line::new(&self.remaining[..line_width]);
self.remaining = rest;
Some(Ok(line))
} else if !self.remaining.is_empty() {
let line = Line::new(self.remaining).trim_end();
self.remaining = b"";
if line.is_empty() {
None
} else {
Some(Ok(line))
}
} else {
None
}
}
}
#[cfg(test)]
#[allow(clippy::unwrap_used)]
mod tests {
use crate::{Base64, Base64Unpadded, Decoder, alphabet::Alphabet, test_vectors::*};
#[cfg(feature = "std")]
use {alloc::vec::Vec, std::io::Read};
#[test]
fn decode_padded() {
decode_test(PADDED_BIN, || {
Decoder::<Base64>::new(PADDED_BASE64.as_bytes()).unwrap()
})
}
#[test]
fn decode_unpadded() {
decode_test(UNPADDED_BIN, || {
Decoder::<Base64Unpadded>::new(UNPADDED_BASE64.as_bytes()).unwrap()
})
}
#[test]
fn decode_multiline_padded() {
decode_test(MULTILINE_PADDED_BIN, || {
Decoder::<Base64>::new_wrapped(MULTILINE_PADDED_BASE64.as_bytes(), 70).unwrap()
})
}
#[test]
fn decode_multiline_unpadded() {
decode_test(MULTILINE_UNPADDED_BIN, || {
Decoder::<Base64Unpadded>::new_wrapped(MULTILINE_UNPADDED_BASE64.as_bytes(), 70)
.unwrap()
})
}
#[cfg(feature = "std")]
#[test]
fn read_multiline_padded() {
let mut decoder =
Decoder::<Base64>::new_wrapped(MULTILINE_PADDED_BASE64.as_bytes(), 70).unwrap();
let mut buf = Vec::new();
let len = decoder.read_to_end(&mut buf).unwrap();
assert_eq!(len, MULTILINE_PADDED_BIN.len());
assert_eq!(buf.as_slice(), MULTILINE_PADDED_BIN);
}
#[cfg(feature = "std")]
#[test]
fn decode_empty_at_end() {
let mut decoder = Decoder::<Base64>::new(b"AAAA").unwrap();
// Strip initial bytes
let mut buf = vec![0u8; 3];
assert_eq!(decoder.decode(&mut buf), Ok(&vec![0u8; 3][..]));
// Now try to read nothing from the end
let mut buf: Vec<u8> = vec![];
assert_eq!(decoder.decode(&mut buf), Ok(&[][..]));
}
/// Core functionality of a decoding test
#[allow(clippy::arithmetic_side_effects)]
fn decode_test<'a, F, V>(expected: &[u8], f: F)
where
F: Fn() -> Decoder<'a, V>,
V: Alphabet,
{
for chunk_size in 1..expected.len() {
let mut decoder = f();
let mut remaining_len = decoder.remaining_len();
let mut buffer = [0u8; 1024];
for chunk in expected.chunks(chunk_size) {
assert!(!decoder.is_finished());
let decoded = decoder.decode(&mut buffer[..chunk.len()]).unwrap();
assert_eq!(chunk, decoded);
let dlen = decoded.len();
remaining_len -= dlen;
assert_eq!(remaining_len, decoder.remaining_len());
}
assert!(decoder.is_finished());
assert_eq!(decoder.remaining_len(), 0);
}
}
}

364
vendor/base64ct/src/encoder.rs vendored Normal file
View File

@@ -0,0 +1,364 @@
//! Buffered Base64 encoder.
use crate::{
Encoding,
Error::{self, InvalidLength},
LineEnding, MIN_LINE_WIDTH,
};
use core::{cmp, marker::PhantomData, str};
#[cfg(feature = "std")]
use std::io;
#[cfg(doc)]
use crate::{Base64, Base64Unpadded};
/// Stateful Base64 encoder with support for buffered, incremental encoding.
///
/// The `E` type parameter can be any type which impls [`Encoding`] such as
/// [`Base64`] or [`Base64Unpadded`].
pub struct Encoder<'o, E: Encoding> {
/// Output buffer.
output: &'o mut [u8],
/// Cursor within the output buffer.
position: usize,
/// Block buffer used for non-block-aligned data.
block_buffer: BlockBuffer,
/// Configuration and state for line-wrapping the output at a specified
/// column.
line_wrapper: Option<LineWrapper>,
/// Phantom parameter for the Base64 encoding in use.
encoding: PhantomData<E>,
}
impl<'o, E: Encoding> Encoder<'o, E> {
/// Create a new encoder which writes output to the given byte slice.
///
/// Output constructed using this method is not line-wrapped.
pub fn new(output: &'o mut [u8]) -> Result<Self, Error> {
if output.is_empty() {
return Err(InvalidLength);
}
Ok(Self {
output,
position: 0,
block_buffer: BlockBuffer::default(),
line_wrapper: None,
encoding: PhantomData,
})
}
/// Create a new encoder which writes line-wrapped output to the given byte
/// slice.
///
/// Output will be wrapped at the specified interval, using the provided
/// line ending. Use [`LineEnding::default()`] to use the conventional line
/// ending for the target OS.
///
/// Minimum allowed line width is 4.
pub fn new_wrapped(
output: &'o mut [u8],
width: usize,
ending: LineEnding,
) -> Result<Self, Error> {
let mut encoder = Self::new(output)?;
encoder.line_wrapper = Some(LineWrapper::new(width, ending)?);
Ok(encoder)
}
/// Encode the provided buffer as Base64, writing it to the output buffer.
///
/// # Returns
/// - `Ok(())` if the expected amount of data was read
/// - `Err(Error::InvalidLength)` if there is insufficient space in the output buffer
pub fn encode(&mut self, mut input: &[u8]) -> Result<(), Error> {
// If there's data in the block buffer, fill it
if !self.block_buffer.is_empty() {
self.process_buffer(&mut input)?;
}
while !input.is_empty() {
// Attempt to encode a stride of block-aligned data
let in_blocks = input.len() / 3;
let out_blocks = self.remaining().len() / 4;
let mut blocks = cmp::min(in_blocks, out_blocks);
// When line wrapping, cap the block-aligned stride at near/at line length
if let Some(line_wrapper) = &self.line_wrapper {
line_wrapper.wrap_blocks(&mut blocks)?;
}
if blocks > 0 {
let len = blocks.checked_mul(3).ok_or(InvalidLength)?;
let (in_aligned, in_rem) = input.split_at(len);
input = in_rem;
self.perform_encode(in_aligned)?;
}
// If there's remaining non-aligned data, fill the block buffer
if !input.is_empty() {
self.process_buffer(&mut input)?;
}
}
Ok(())
}
/// Get the position inside of the output buffer where the write cursor
/// is currently located.
pub fn position(&self) -> usize {
self.position
}
/// Finish encoding data, returning the resulting Base64 as a `str`.
pub fn finish(self) -> Result<&'o str, Error> {
self.finish_with_remaining().map(|(base64, _)| base64)
}
/// Finish encoding data, returning the resulting Base64 as a `str`
/// along with the remaining space in the output buffer.
pub fn finish_with_remaining(mut self) -> Result<(&'o str, &'o mut [u8]), Error> {
if !self.block_buffer.is_empty() {
let buffer_len = self.block_buffer.position;
let block = self.block_buffer.bytes;
self.perform_encode(&block[..buffer_len])?;
}
let (base64, remaining) = self.output.split_at_mut(self.position);
Ok((str::from_utf8(base64)?, remaining))
}
/// Borrow the remaining data in the buffer.
fn remaining(&mut self) -> &mut [u8] {
&mut self.output[self.position..]
}
/// Fill the block buffer with data, consuming and encoding it when the
/// buffer is full.
fn process_buffer(&mut self, input: &mut &[u8]) -> Result<(), Error> {
self.block_buffer.fill(input)?;
if self.block_buffer.is_full() {
let block = self.block_buffer.take();
self.perform_encode(&block)?;
}
Ok(())
}
/// Perform Base64 encoding operation.
fn perform_encode(&mut self, input: &[u8]) -> Result<usize, Error> {
let mut len = E::encode(input, self.remaining())?.len();
// Insert newline characters into the output as needed
if let Some(line_wrapper) = &mut self.line_wrapper {
line_wrapper.insert_newlines(&mut self.output[self.position..], &mut len)?;
}
self.position = self.position.checked_add(len).ok_or(InvalidLength)?;
Ok(len)
}
}
#[cfg(feature = "std")]
impl<E: Encoding> io::Write for Encoder<'_, E> {
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
self.encode(buf)?;
Ok(buf.len())
}
fn flush(&mut self) -> io::Result<()> {
// TODO(tarcieri): return an error if there's still data remaining in the buffer?
Ok(())
}
}
/// Base64 encode buffer for a 1-block output.
///
/// This handles a partial block of data, i.e. data which hasn't been
#[derive(Clone, Default, Debug)]
struct BlockBuffer {
/// 3 decoded bytes to be encoded to a 4-byte Base64-encoded input.
bytes: [u8; Self::SIZE],
/// Position within the buffer.
position: usize,
}
impl BlockBuffer {
/// Size of the buffer in bytes: 3-bytes of unencoded input which
/// Base64 encode to 4-bytes of output.
const SIZE: usize = 3;
/// Fill the remaining space in the buffer with the input data.
fn fill(&mut self, input: &mut &[u8]) -> Result<(), Error> {
let remaining = Self::SIZE.checked_sub(self.position).ok_or(InvalidLength)?;
let len = cmp::min(input.len(), remaining);
self.bytes[self.position..][..len].copy_from_slice(&input[..len]);
self.position = self.position.checked_add(len).ok_or(InvalidLength)?;
*input = &input[len..];
Ok(())
}
/// Take the output buffer, resetting the position to 0.
fn take(&mut self) -> [u8; Self::SIZE] {
debug_assert!(self.is_full());
let result = self.bytes;
*self = Default::default();
result
}
/// Is the buffer empty?
fn is_empty(&self) -> bool {
self.position == 0
}
/// Is the buffer full?
fn is_full(&self) -> bool {
self.position == Self::SIZE
}
}
/// Helper for wrapping Base64 at a given line width.
#[derive(Debug)]
struct LineWrapper {
/// Number of bytes remaining in the current line.
remaining: usize,
/// Column at which Base64 should be wrapped.
width: usize,
/// Newline characters to use at the end of each line.
ending: LineEnding,
}
impl LineWrapper {
/// Create a new linewrapper.
fn new(width: usize, ending: LineEnding) -> Result<Self, Error> {
if width < MIN_LINE_WIDTH {
return Err(InvalidLength);
}
Ok(Self {
remaining: width,
width,
ending,
})
}
/// Wrap the number of blocks to encode near/at EOL.
fn wrap_blocks(&self, blocks: &mut usize) -> Result<(), Error> {
if blocks.checked_mul(4).ok_or(InvalidLength)? >= self.remaining {
*blocks = self.remaining / 4;
}
Ok(())
}
/// Insert newlines into the output buffer as needed.
fn insert_newlines(&mut self, mut buffer: &mut [u8], len: &mut usize) -> Result<(), Error> {
let mut buffer_len = *len;
if buffer_len <= self.remaining {
self.remaining = self
.remaining
.checked_sub(buffer_len)
.ok_or(InvalidLength)?;
return Ok(());
}
buffer = &mut buffer[self.remaining..];
buffer_len = buffer_len
.checked_sub(self.remaining)
.ok_or(InvalidLength)?;
// The `wrap_blocks` function should ensure the buffer is no larger than a Base64 block
debug_assert!(buffer_len <= 4, "buffer too long: {buffer_len}");
// Ensure space in buffer to add newlines
let buffer_end = buffer_len
.checked_add(self.ending.len())
.ok_or(InvalidLength)?;
if buffer_end >= buffer.len() {
return Err(InvalidLength);
}
// Shift the buffer contents to make space for the line ending
for i in (0..buffer_len).rev() {
buffer[i.checked_add(self.ending.len()).ok_or(InvalidLength)?] = buffer[i];
}
buffer[..self.ending.len()].copy_from_slice(self.ending.as_bytes());
*len = (*len).checked_add(self.ending.len()).ok_or(InvalidLength)?;
self.remaining = self.width.checked_sub(buffer_len).ok_or(InvalidLength)?;
Ok(())
}
}
#[cfg(test)]
#[allow(clippy::unwrap_used)]
mod tests {
use crate::{Base64, Base64Unpadded, Encoder, LineEnding, alphabet::Alphabet, test_vectors::*};
#[test]
fn encode_padded() {
encode_test::<Base64>(PADDED_BIN, PADDED_BASE64, None);
}
#[test]
fn encode_unpadded() {
encode_test::<Base64Unpadded>(UNPADDED_BIN, UNPADDED_BASE64, None);
}
#[test]
fn encode_multiline_padded() {
encode_test::<Base64>(MULTILINE_PADDED_BIN, MULTILINE_PADDED_BASE64, Some(70));
}
#[test]
fn encode_multiline_unpadded() {
encode_test::<Base64Unpadded>(MULTILINE_UNPADDED_BIN, MULTILINE_UNPADDED_BASE64, Some(70));
}
#[test]
fn no_trailing_newline_when_aligned() {
let mut buffer = [0u8; 64];
let mut encoder = Encoder::<Base64>::new_wrapped(&mut buffer, 64, LineEnding::LF).unwrap();
encoder.encode(&[0u8; 48]).unwrap();
// Ensure no newline character is present in this case
assert_eq!(
"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA",
encoder.finish().unwrap()
);
}
/// Core functionality of an encoding test.
fn encode_test<V: Alphabet>(input: &[u8], expected: &str, wrapped: Option<usize>) {
let mut buffer = [0u8; 1024];
for chunk_size in 1..input.len() {
let mut encoder = match wrapped {
Some(line_width) => {
Encoder::<V>::new_wrapped(&mut buffer, line_width, LineEnding::LF)
}
None => Encoder::<V>::new(&mut buffer),
}
.unwrap();
for chunk in input.chunks(chunk_size) {
encoder.encode(chunk).unwrap();
}
assert_eq!(expected, encoder.finish().unwrap());
}
}
}

376
vendor/base64ct/src/encoding.rs vendored Normal file
View File

@@ -0,0 +1,376 @@
//! Base64 encodings
use crate::{
alphabet::Alphabet,
errors::{Error, InvalidEncodingError, InvalidLengthError},
};
use core::str;
#[cfg(feature = "alloc")]
use alloc::{string::String, vec::Vec};
#[cfg(doc)]
use crate::{Base64, Base64Bcrypt, Base64Crypt, Base64Unpadded, Base64Url, Base64UrlUnpadded};
/// Padding character
const PAD: u8 = b'=';
/// Base64 encoding trait.
///
/// This trait must be imported to make use of any Base64 alphabet defined
/// in this crate.
///
/// The following encoding types impl this trait:
///
/// - [`Base64`]: standard Base64 encoding with `=` padding.
/// - [`Base64Bcrypt`]: bcrypt Base64 encoding.
/// - [`Base64Crypt`]: `crypt(3)` Base64 encoding.
/// - [`Base64Unpadded`]: standard Base64 encoding *without* padding.
/// - [`Base64Url`]: URL-safe Base64 encoding with `=` padding.
/// - [`Base64UrlUnpadded`]: URL-safe Base64 encoding *without* padding.
pub trait Encoding: Alphabet {
/// Decode a Base64 string into the provided destination buffer.
fn decode(src: impl AsRef<[u8]>, dst: &mut [u8]) -> Result<&[u8], Error>;
/// Decode a Base64 string in-place.
///
/// NOTE: this method does not (yet) validate that padding is well-formed,
/// if the given Base64 encoding is padded.
fn decode_in_place(buf: &mut [u8]) -> Result<&[u8], InvalidEncodingError>;
/// Decode a Base64 string into a byte vector.
#[cfg(feature = "alloc")]
fn decode_vec(input: &str) -> Result<Vec<u8>, Error>;
/// Encode the input byte slice as Base64.
///
/// Writes the result into the provided destination slice, returning an
/// ASCII-encoded Base64 string value.
fn encode<'a>(src: &[u8], dst: &'a mut [u8]) -> Result<&'a str, InvalidLengthError>;
/// Encode input byte slice into a [`String`] containing Base64.
///
/// # Panics
/// If `input` length is greater than `usize::MAX/4`.
#[cfg(feature = "alloc")]
fn encode_string(input: &[u8]) -> String;
/// Get the length of Base64 produced by encoding the given bytes.
///
/// WARNING: this function will return `0` for lengths greater than `usize::MAX/4`!
fn encoded_len(bytes: &[u8]) -> usize;
}
impl<T: Alphabet> Encoding for T {
fn decode(src: impl AsRef<[u8]>, dst: &mut [u8]) -> Result<&[u8], Error> {
let (src_unpadded, mut err) = if T::PADDED {
let (unpadded_len, e) = decode_padding(src.as_ref())?;
(&src.as_ref()[..unpadded_len], e)
} else {
(src.as_ref(), 0)
};
let dlen = decoded_len(src_unpadded.len());
if dlen > dst.len() {
return Err(Error::InvalidLength);
}
let dst = &mut dst[..dlen];
let mut src_chunks = src_unpadded.chunks_exact(4);
let mut dst_chunks = dst.chunks_exact_mut(3);
for (s, d) in (&mut src_chunks).zip(&mut dst_chunks) {
err |= Self::decode_3bytes(s, d);
}
let src_rem = src_chunks.remainder();
let dst_rem = dst_chunks.into_remainder();
err |= !(src_rem.is_empty() || src_rem.len() >= 2) as i16;
let mut tmp_out = [0u8; 3];
let mut tmp_in = [b'A'; 4];
tmp_in[..src_rem.len()].copy_from_slice(src_rem);
err |= Self::decode_3bytes(&tmp_in, &mut tmp_out);
dst_rem.copy_from_slice(&tmp_out[..dst_rem.len()]);
if err == 0 {
validate_last_block::<T>(src.as_ref(), dst)?;
Ok(dst)
} else {
Err(Error::InvalidEncoding)
}
}
// TODO(tarcieri): explicitly checked/wrapped arithmetic
#[allow(clippy::arithmetic_side_effects)]
fn decode_in_place(mut buf: &mut [u8]) -> Result<&[u8], InvalidEncodingError> {
// TODO: eliminate unsafe code when LLVM12 is stable
// See: https://github.com/rust-lang/rust/issues/80963
let mut err = if T::PADDED {
let (unpadded_len, e) = decode_padding(buf)?;
buf = &mut buf[..unpadded_len];
e
} else {
0
};
let dlen = decoded_len(buf.len());
let full_chunks = buf.len() / 4;
for chunk in 0..full_chunks {
// SAFETY: `p3` and `p4` point inside `buf`, while they may overlap,
// read and write are clearly separated from each other and done via
// raw pointers.
#[allow(unsafe_code)]
unsafe {
debug_assert!(3 * chunk + 3 <= buf.len());
debug_assert!(4 * chunk + 4 <= buf.len());
let p3 = buf.as_mut_ptr().add(3 * chunk) as *mut [u8; 3];
let p4 = buf.as_ptr().add(4 * chunk) as *const [u8; 4];
let mut tmp_out = [0u8; 3];
err |= Self::decode_3bytes(&*p4, &mut tmp_out);
*p3 = tmp_out;
}
}
let src_rem_pos = 4 * full_chunks;
let src_rem_len = buf.len() - src_rem_pos;
let dst_rem_pos = 3 * full_chunks;
let dst_rem_len = dlen - dst_rem_pos;
err |= !(src_rem_len == 0 || src_rem_len >= 2) as i16;
let mut tmp_in = [b'A'; 4];
tmp_in[..src_rem_len].copy_from_slice(&buf[src_rem_pos..]);
let mut tmp_out = [0u8; 3];
err |= Self::decode_3bytes(&tmp_in, &mut tmp_out);
if err == 0 {
// SAFETY: `dst_rem_len` is always smaller than 4, so we don't
// read outside of `tmp_out`, write and the final slicing never go
// outside of `buf`.
#[allow(unsafe_code)]
unsafe {
debug_assert!(dst_rem_pos + dst_rem_len <= buf.len());
debug_assert!(dst_rem_len <= tmp_out.len());
debug_assert!(dlen <= buf.len());
core::ptr::copy_nonoverlapping(
tmp_out.as_ptr(),
buf.as_mut_ptr().add(dst_rem_pos),
dst_rem_len,
);
Ok(buf.get_unchecked(..dlen))
}
} else {
Err(InvalidEncodingError)
}
}
#[cfg(feature = "alloc")]
fn decode_vec(input: &str) -> Result<Vec<u8>, Error> {
let mut output = vec![0u8; decoded_len(input.len())];
let len = Self::decode(input, &mut output)?.len();
if len <= output.len() {
output.truncate(len);
Ok(output)
} else {
Err(Error::InvalidLength)
}
}
fn encode<'a>(src: &[u8], dst: &'a mut [u8]) -> Result<&'a str, InvalidLengthError> {
let elen = match encoded_len_inner(src.len(), T::PADDED) {
Some(v) => v,
None => return Err(InvalidLengthError),
};
if elen > dst.len() {
return Err(InvalidLengthError);
}
let dst = &mut dst[..elen];
let mut src_chunks = src.chunks_exact(3);
let mut dst_chunks = dst.chunks_exact_mut(4);
for (s, d) in (&mut src_chunks).zip(&mut dst_chunks) {
Self::encode_3bytes(s, d);
}
let src_rem = src_chunks.remainder();
if T::PADDED {
if let Some(dst_rem) = dst_chunks.next() {
let mut tmp = [0u8; 3];
tmp[..src_rem.len()].copy_from_slice(src_rem);
Self::encode_3bytes(&tmp, dst_rem);
let flag = src_rem.len() == 1;
let mask = (flag as u8).wrapping_sub(1);
dst_rem[2] = (dst_rem[2] & mask) | (PAD & !mask);
dst_rem[3] = PAD;
}
} else {
let dst_rem = dst_chunks.into_remainder();
let mut tmp_in = [0u8; 3];
let mut tmp_out = [0u8; 4];
tmp_in[..src_rem.len()].copy_from_slice(src_rem);
Self::encode_3bytes(&tmp_in, &mut tmp_out);
dst_rem.copy_from_slice(&tmp_out[..dst_rem.len()]);
}
debug_assert!(str::from_utf8(dst).is_ok());
// SAFETY: values written by `encode_3bytes` are valid one-byte UTF-8 chars
#[allow(unsafe_code)]
Ok(unsafe { str::from_utf8_unchecked(dst) })
}
#[cfg(feature = "alloc")]
fn encode_string(input: &[u8]) -> String {
let elen = encoded_len_inner(input.len(), T::PADDED).expect("input is too big");
let mut dst = vec![0u8; elen];
let res = Self::encode(input, &mut dst).expect("encoding error");
debug_assert_eq!(elen, res.len());
debug_assert!(str::from_utf8(&dst).is_ok());
// SAFETY: `dst` is fully written and contains only valid one-byte UTF-8 chars
#[allow(unsafe_code)]
unsafe {
String::from_utf8_unchecked(dst)
}
}
fn encoded_len(bytes: &[u8]) -> usize {
encoded_len_inner(bytes.len(), T::PADDED).unwrap_or(0)
}
}
/// Validate padding is of the expected length compute unpadded length.
///
/// Note that this method does not explicitly check that the padded data
/// is valid in and of itself: that is performed by `validate_last_block` as a
/// final step.
///
/// Returns length-related errors eagerly as a [`Result`], and data-dependent
/// errors (i.e. malformed padding bytes) as `i16` to be combined with other
/// encoding-related errors prior to branching.
#[inline(always)]
pub(crate) fn decode_padding(input: &[u8]) -> Result<(usize, i16), InvalidEncodingError> {
if input.len() % 4 != 0 {
return Err(InvalidEncodingError);
}
let unpadded_len = match *input {
[.., b0, b1] => is_pad_ct(b0)
.checked_add(is_pad_ct(b1))
.and_then(|len| len.try_into().ok())
.and_then(|len| input.len().checked_sub(len))
.ok_or(InvalidEncodingError)?,
_ => input.len(),
};
let padding_len = input
.len()
.checked_sub(unpadded_len)
.ok_or(InvalidEncodingError)?;
let err = match *input {
[.., b0] if padding_len == 1 => is_pad_ct(b0) ^ 1,
[.., b0, b1] if padding_len == 2 => (is_pad_ct(b0) & is_pad_ct(b1)) ^ 1,
_ => {
if padding_len == 0 {
0
} else {
return Err(InvalidEncodingError);
}
}
};
Ok((unpadded_len, err))
}
/// Validate that the last block of the decoded data round-trips back to the
/// encoded data.
fn validate_last_block<T: Alphabet>(encoded: &[u8], decoded: &[u8]) -> Result<(), Error> {
if encoded.is_empty() && decoded.is_empty() {
return Ok(());
}
// TODO(tarcieri): explicitly checked/wrapped arithmetic
#[allow(clippy::arithmetic_side_effects)]
fn last_block_start(bytes: &[u8], block_size: usize) -> usize {
(bytes.len().saturating_sub(1) / block_size) * block_size
}
let enc_block = encoded
.get(last_block_start(encoded, 4)..)
.ok_or(Error::InvalidEncoding)?;
let dec_block = decoded
.get(last_block_start(decoded, 3)..)
.ok_or(Error::InvalidEncoding)?;
// Round-trip encode the decoded block
let mut buf = [0u8; 4];
let block = T::encode(dec_block, &mut buf)?;
// Non-short-circuiting comparison of padding
// TODO(tarcieri): better constant-time mechanisms (e.g. `subtle`)?
if block
.as_bytes()
.iter()
.zip(enc_block.iter())
.fold(0, |acc, (a, b)| acc | (a ^ b))
== 0
{
Ok(())
} else {
Err(Error::InvalidEncoding)
}
}
/// Get the length of the output from decoding the provided *unpadded*
/// Base64-encoded input.
///
/// Note that this function does not fully validate the Base64 is well-formed
/// and may return incorrect results for malformed Base64.
// TODO(tarcieri): explicitly checked/wrapped arithmetic
#[allow(clippy::arithmetic_side_effects)]
#[inline(always)]
pub(crate) fn decoded_len(input_len: usize) -> usize {
// overflow-proof computation of `(3*n)/4`
let k = input_len / 4;
let l = input_len - 4 * k;
3 * k + (3 * l) / 4
}
/// Branchless match that a given byte is the `PAD` character
// TODO(tarcieri): explicitly checked/wrapped arithmetic
#[allow(clippy::arithmetic_side_effects)]
#[inline(always)]
fn is_pad_ct(input: u8) -> i16 {
((((PAD as i16 - 1) - input as i16) & (input as i16 - (PAD as i16 + 1))) >> 8) & 1
}
// TODO(tarcieri): explicitly checked/wrapped arithmetic
#[allow(clippy::arithmetic_side_effects)]
#[inline(always)]
const fn encoded_len_inner(n: usize, padded: bool) -> Option<usize> {
match n.checked_mul(4) {
Some(q) => {
if padded {
Some(((q / 3) + 3) & !3)
} else {
Some((q / 3) + (q % 3 != 0) as usize)
}
}
None => None,
}
}

81
vendor/base64ct/src/errors.rs vendored Normal file
View File

@@ -0,0 +1,81 @@
//! Error types
use core::fmt;
const INVALID_ENCODING_MSG: &str = "invalid Base64 encoding";
const INVALID_LENGTH_MSG: &str = "invalid Base64 length";
/// Insufficient output buffer length.
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub struct InvalidLengthError;
impl fmt::Display for InvalidLengthError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
f.write_str(INVALID_LENGTH_MSG)
}
}
impl core::error::Error for InvalidLengthError {}
/// Invalid encoding of provided Base64 string.
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub struct InvalidEncodingError;
impl fmt::Display for InvalidEncodingError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
f.write_str(INVALID_ENCODING_MSG)
}
}
impl core::error::Error for InvalidEncodingError {}
/// Generic error, union of [`InvalidLengthError`] and [`InvalidEncodingError`].
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub enum Error {
/// Invalid encoding of provided Base64 string.
InvalidEncoding,
/// Insufficient output buffer length.
InvalidLength,
}
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
let s = match self {
Self::InvalidEncoding => INVALID_ENCODING_MSG,
Self::InvalidLength => INVALID_LENGTH_MSG,
};
f.write_str(s)
}
}
impl From<InvalidEncodingError> for Error {
#[inline]
fn from(_: InvalidEncodingError) -> Error {
Error::InvalidEncoding
}
}
impl From<InvalidLengthError> for Error {
#[inline]
fn from(_: InvalidLengthError) -> Error {
Error::InvalidLength
}
}
impl From<core::str::Utf8Error> for Error {
#[inline]
fn from(_: core::str::Utf8Error) -> Error {
Error::InvalidEncoding
}
}
#[cfg(feature = "std")]
impl From<Error> for std::io::Error {
fn from(err: Error) -> std::io::Error {
// TODO(tarcieri): better customize `ErrorKind`?
std::io::Error::new(std::io::ErrorKind::InvalidData, err)
}
}
impl core::error::Error for Error {}

108
vendor/base64ct/src/lib.rs vendored Normal file
View File

@@ -0,0 +1,108 @@
#![no_std]
#![cfg_attr(docsrs, feature(doc_cfg))]
#![doc(
html_logo_url = "https://raw.githubusercontent.com/RustCrypto/media/6ee8e381/logo.svg",
html_favicon_url = "https://raw.githubusercontent.com/RustCrypto/media/6ee8e381/logo.svg"
)]
#![doc = include_str!("../README.md")]
#![warn(
clippy::arithmetic_side_effects,
clippy::mod_module_files,
clippy::panic,
clippy::panic_in_result_fn,
clippy::unwrap_used,
missing_docs,
rust_2018_idioms,
unsafe_code,
unused_lifetimes,
unused_qualifications
)]
//! # Usage
//!
//! ## Allocating (enable `alloc` crate feature)
//!
//! ```
//! # #[cfg(feature = "alloc")]
//! # {
//! use base64ct::{Base64, Encoding};
//!
//! let bytes = b"example bytestring!";
//! let encoded = Base64::encode_string(bytes);
//! assert_eq!(encoded, "ZXhhbXBsZSBieXRlc3RyaW5nIQ==");
//!
//! let decoded = Base64::decode_vec(&encoded).unwrap();
//! assert_eq!(decoded, bytes);
//! # }
//! ```
//!
//! ## Heapless `no_std` usage
//!
//! ```
//! use base64ct::{Base64, Encoding};
//!
//! const BUF_SIZE: usize = 128;
//!
//! let bytes = b"example bytestring!";
//! assert!(Base64::encoded_len(bytes) <= BUF_SIZE);
//!
//! let mut enc_buf = [0u8; BUF_SIZE];
//! let encoded = Base64::encode(bytes, &mut enc_buf).unwrap();
//! assert_eq!(encoded, "ZXhhbXBsZSBieXRlc3RyaW5nIQ==");
//!
//! let mut dec_buf = [0u8; BUF_SIZE];
//! let decoded = Base64::decode(encoded, &mut dec_buf).unwrap();
//! assert_eq!(decoded, bytes);
//! ```
//!
//! # Implementation
//!
//! Implemented using integer arithmetic alone without any lookup tables or
//! data-dependent branches, thereby providing portable "best effort"
//! constant-time operation.
//!
//! Not constant-time with respect to message length (only data).
//!
//! Adapted from the following constant-time C++ implementation of Base64:
//!
//! <https://github.com/Sc00bz/ConstTimeEncoding/blob/master/base64.cpp>
//!
//! Copyright (c) 2014 Steve "Sc00bz" Thomas (steve at tobtu dot com).
//! Derived code is dual licensed MIT + Apache 2 (with permission from Sc00bz).
#[cfg(feature = "alloc")]
#[macro_use]
extern crate alloc;
#[cfg(feature = "std")]
extern crate std;
mod alphabet;
mod decoder;
mod encoder;
mod encoding;
mod errors;
mod line_ending;
#[cfg(test)]
mod test_vectors;
pub use crate::{
alphabet::{
bcrypt::Base64Bcrypt,
pbkdf2::Base64Pbkdf2,
shacrypt::Base64ShaCrypt,
standard::{Base64, Base64Unpadded},
url::{Base64Url, Base64UrlUnpadded},
},
decoder::Decoder,
encoder::Encoder,
encoding::Encoding,
errors::{Error, InvalidEncodingError, InvalidLengthError},
line_ending::LineEnding,
};
#[allow(deprecated)]
pub use crate::alphabet::crypt::Base64Crypt;
/// Minimum supported line width.
const MIN_LINE_WIDTH: usize = 4;

53
vendor/base64ct/src/line_ending.rs vendored Normal file
View File

@@ -0,0 +1,53 @@
//! Line endings.
/// Carriage return
pub(crate) const CHAR_CR: u8 = 0x0d;
/// Line feed
pub(crate) const CHAR_LF: u8 = 0x0a;
/// Line endings: variants of newline characters that can be used with Base64.
///
/// Use [`LineEnding::default`] to get an appropriate line ending for the
/// current operating system.
#[allow(clippy::upper_case_acronyms)]
#[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord)]
pub enum LineEnding {
/// Carriage return: `\r` (Pre-OS X Macintosh)
CR,
/// Line feed: `\n` (Unix OSes)
LF,
/// Carriage return + line feed: `\r\n` (Windows)
CRLF,
}
impl Default for LineEnding {
// Default line ending matches conventions for target OS
#[cfg(windows)]
fn default() -> LineEnding {
LineEnding::CRLF
}
#[cfg(not(windows))]
fn default() -> LineEnding {
LineEnding::LF
}
}
#[allow(clippy::len_without_is_empty)]
impl LineEnding {
/// Get the byte serialization of this [`LineEnding`].
pub fn as_bytes(self) -> &'static [u8] {
match self {
LineEnding::CR => &[CHAR_CR],
LineEnding::LF => &[CHAR_LF],
LineEnding::CRLF => &[CHAR_CR, CHAR_LF],
}
}
/// Get the encoded length of this [`LineEnding`].
pub fn len(self) -> usize {
self.as_bytes().len()
}
}

67
vendor/base64ct/src/test_vectors.rs vendored Normal file
View File

@@ -0,0 +1,67 @@
//! Base64 test vectors.
/// Padded Base64-encoded example
pub(crate) const PADDED_BASE64: &str = "AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBHwf2HMM5TRXvo2SQJjsNkiDD5KqiiNjrGVv3UUh+mMT5RHxiRtOnlqvjhQtBq0VpmpCV/PwUdhOig4vkbqAcEc=";
pub(crate) const PADDED_BIN: &[u8] = &[
0, 0, 0, 19, 101, 99, 100, 115, 97, 45, 115, 104, 97, 50, 45, 110, 105, 115, 116, 112, 50, 53,
54, 0, 0, 0, 8, 110, 105, 115, 116, 112, 50, 53, 54, 0, 0, 0, 65, 4, 124, 31, 216, 115, 12,
229, 52, 87, 190, 141, 146, 64, 152, 236, 54, 72, 131, 15, 146, 170, 138, 35, 99, 172, 101,
111, 221, 69, 33, 250, 99, 19, 229, 17, 241, 137, 27, 78, 158, 90, 175, 142, 20, 45, 6, 173,
21, 166, 106, 66, 87, 243, 240, 81, 216, 78, 138, 14, 47, 145, 186, 128, 112, 71,
];
/// Unpadded Base64-encoded example
pub(crate) const UNPADDED_BASE64: &str =
"AAAAC3NzaC1lZDI1NTE5AAAAILM+rvN+ot98qgEN796jTiQfZfG1KaT0PtFDJ/XFSqti";
pub(crate) const UNPADDED_BIN: &[u8] = &[
0, 0, 0, 11, 115, 115, 104, 45, 101, 100, 50, 53, 53, 49, 57, 0, 0, 0, 32, 179, 62, 174, 243,
126, 162, 223, 124, 170, 1, 13, 239, 222, 163, 78, 36, 31, 101, 241, 181, 41, 164, 244, 62,
209, 67, 39, 245, 197, 74, 171, 98,
];
/// Padded multi-line Base64 example (from the `ssh-key` crate's `id_ed25519`)
pub(crate) const MULTILINE_PADDED_BASE64: &str = "b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAMwAAAAtzc2gtZW\n\
QyNTUxOQAAACCzPq7zfqLffKoBDe/eo04kH2XxtSmk9D7RQyf1xUqrYgAAAJgAIAxdACAM\n\
XQAAAAtzc2gtZWQyNTUxOQAAACCzPq7zfqLffKoBDe/eo04kH2XxtSmk9D7RQyf1xUqrYg\n\
AAAEC2BsIi0QwW2uFscKTUUXNHLsYX4FxlaSDSblbAj7WR7bM+rvN+ot98qgEN796jTiQf\n\
ZfG1KaT0PtFDJ/XFSqtiAAAAEHVzZXJAZXhhbXBsZS5jb20BAgMEBQ==";
pub(crate) const MULTILINE_PADDED_BIN: &[u8] = &[
111, 112, 101, 110, 115, 115, 104, 45, 107, 101, 121, 45, 118, 49, 0, 0, 0, 0, 4, 110, 111,
110, 101, 0, 0, 0, 4, 110, 111, 110, 101, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 51, 0, 0, 0, 11,
115, 115, 104, 45, 101, 100, 50, 53, 53, 49, 57, 0, 0, 0, 32, 179, 62, 174, 243, 126, 162, 223,
124, 170, 1, 13, 239, 222, 163, 78, 36, 31, 101, 241, 181, 41, 164, 244, 62, 209, 67, 39, 245,
197, 74, 171, 98, 0, 0, 0, 152, 0, 32, 12, 93, 0, 32, 12, 93, 0, 0, 0, 11, 115, 115, 104, 45,
101, 100, 50, 53, 53, 49, 57, 0, 0, 0, 32, 179, 62, 174, 243, 126, 162, 223, 124, 170, 1, 13,
239, 222, 163, 78, 36, 31, 101, 241, 181, 41, 164, 244, 62, 209, 67, 39, 245, 197, 74, 171, 98,
0, 0, 0, 64, 182, 6, 194, 34, 209, 12, 22, 218, 225, 108, 112, 164, 212, 81, 115, 71, 46, 198,
23, 224, 92, 101, 105, 32, 210, 110, 86, 192, 143, 181, 145, 237, 179, 62, 174, 243, 126, 162,
223, 124, 170, 1, 13, 239, 222, 163, 78, 36, 31, 101, 241, 181, 41, 164, 244, 62, 209, 67, 39,
245, 197, 74, 171, 98, 0, 0, 0, 16, 117, 115, 101, 114, 64, 101, 120, 97, 109, 112, 108, 101,
46, 99, 111, 109, 1, 2, 3, 4, 5,
];
/// Unpadded multi-line Base64 example (from the `ssh-key` crate's `id_ecdsa_p256`).
pub(crate) const MULTILINE_UNPADDED_BASE64: &str = "b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAaAAAABNlY2RzYS\n\
1zaGEyLW5pc3RwMjU2AAAACG5pc3RwMjU2AAAAQQR8H9hzDOU0V76NkkCY7DZIgw+Sqooj\n\
Y6xlb91FIfpjE+UR8YkbTp5ar44ULQatFaZqQlfz8FHYTooOL5G6gHBHAAAAsB8RBhUfEQ\n\
YVAAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBHwf2HMM5TRXvo2S\n\
QJjsNkiDD5KqiiNjrGVv3UUh+mMT5RHxiRtOnlqvjhQtBq0VpmpCV/PwUdhOig4vkbqAcE\n\
cAAAAhAMp4pkd0v643EjIkk38DmJYBiXB6ygqGRc60NZxCO6B5AAAAEHVzZXJAZXhhbXBs\n\
ZS5jb20BAgMEBQYH";
pub(crate) const MULTILINE_UNPADDED_BIN: &[u8] = &[
111, 112, 101, 110, 115, 115, 104, 45, 107, 101, 121, 45, 118, 49, 0, 0, 0, 0, 4, 110, 111,
110, 101, 0, 0, 0, 4, 110, 111, 110, 101, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 104, 0, 0, 0, 19,
101, 99, 100, 115, 97, 45, 115, 104, 97, 50, 45, 110, 105, 115, 116, 112, 50, 53, 54, 0, 0, 0,
8, 110, 105, 115, 116, 112, 50, 53, 54, 0, 0, 0, 65, 4, 124, 31, 216, 115, 12, 229, 52, 87,
190, 141, 146, 64, 152, 236, 54, 72, 131, 15, 146, 170, 138, 35, 99, 172, 101, 111, 221, 69,
33, 250, 99, 19, 229, 17, 241, 137, 27, 78, 158, 90, 175, 142, 20, 45, 6, 173, 21, 166, 106,
66, 87, 243, 240, 81, 216, 78, 138, 14, 47, 145, 186, 128, 112, 71, 0, 0, 0, 176, 31, 17, 6,
21, 31, 17, 6, 21, 0, 0, 0, 19, 101, 99, 100, 115, 97, 45, 115, 104, 97, 50, 45, 110, 105, 115,
116, 112, 50, 53, 54, 0, 0, 0, 8, 110, 105, 115, 116, 112, 50, 53, 54, 0, 0, 0, 65, 4, 124, 31,
216, 115, 12, 229, 52, 87, 190, 141, 146, 64, 152, 236, 54, 72, 131, 15, 146, 170, 138, 35, 99,
172, 101, 111, 221, 69, 33, 250, 99, 19, 229, 17, 241, 137, 27, 78, 158, 90, 175, 142, 20, 45,
6, 173, 21, 166, 106, 66, 87, 243, 240, 81, 216, 78, 138, 14, 47, 145, 186, 128, 112, 71, 0, 0,
0, 33, 0, 202, 120, 166, 71, 116, 191, 174, 55, 18, 50, 36, 147, 127, 3, 152, 150, 1, 137, 112,
122, 202, 10, 134, 69, 206, 180, 53, 156, 66, 59, 160, 121, 0, 0, 0, 16, 117, 115, 101, 114,
64, 101, 120, 97, 109, 112, 108, 101, 46, 99, 111, 109, 1, 2, 3, 4, 5, 6, 7,
];

68
vendor/base64ct/tests/bcrypt.rs vendored Normal file
View File

@@ -0,0 +1,68 @@
//! bcrypt Base64 tests
#[macro_use]
mod common;
use crate::common::*;
use base64ct::Base64Bcrypt;
const TEST_VECTORS: &[TestVector] = &[
TestVector { raw: b"", b64: "" },
TestVector {
raw: b"\0",
b64: "..",
},
TestVector {
raw: b"***",
b64: "Igmo",
},
TestVector {
raw: b"\x01\x02\x03\x04",
b64: ".OGB/.",
},
TestVector {
raw: b"\xAD\xAD\xAD\xAD\xAD",
b64: "pY0rpYy",
},
TestVector {
raw: b"\xFF\xEF\xFE\xFF\xEF\xFE",
b64: "98989898",
},
TestVector {
raw: b"\xFF\xFF\xFF\xFF\xFF",
b64: "9999996",
},
TestVector {
raw: b"\x40\xC1\x3F\xBD\x05\x4C\x72\x2A\xA3\xC2\xF2\x11\x73\xC0\x69\xEA\
\x49\x7D\x35\x29\x6B\xCC\x24\x65\xF6\xF9\xD0\x41\x08\x7B\xD7\xA9",
b64: "OKC9tOTKagohutGPa6/n4ij7LQjpxAPj7tlOOOf5z4i",
},
TestVector {
raw: b"\x00\x10\x83\x10Q\x87 \x92\x8B0\xD3\x8FA\x14\x93QU\x97a\x96\x9Bq\
\xD7\x9F\x82\x18\xA3\x92Y\xA7\xA2\x9A\xAB\xB2\xDB\xAF\xC3\x1C\xB3\
\xFB\xF0\x00",
b64: "./ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwx89..",
},
];
impl_tests!(Base64Bcrypt);
#[test]
fn reject_trailing_whitespace() {
let input = "OKC9tOTKagohutGPa6/n4ij7LQjpxAPj7tlOOOf5z4i\n";
let mut buf = [0u8; 1024];
assert_eq!(
Base64Bcrypt::decode(input, &mut buf),
Err(Error::InvalidEncoding)
);
}
#[test]
fn unpadded_reject_trailing_equals() {
let input = "OKC9tOTKagohutGPa6/n4ij7LQjpxAPj7tlOOOf5z4i=";
let mut buf = [0u8; 1024];
assert_eq!(
Base64Bcrypt::decode(input, &mut buf),
Err(Error::InvalidEncoding)
);
}

80
vendor/base64ct/tests/common/mod.rs vendored Normal file
View File

@@ -0,0 +1,80 @@
//! Common testing functionality
/// Base64 test vector
pub struct TestVector {
pub raw: &'static [u8],
pub b64: &'static str,
}
/// Generate test suite for a particular Base64 flavor
#[macro_export]
macro_rules! impl_tests {
($encoding:ty) => {
use base64ct::{Encoding, Error};
#[test]
fn encode_test_vectors() {
let mut buf = [0u8; 1024];
for vector in TEST_VECTORS {
let out = <$encoding>::encode(vector.raw, &mut buf).unwrap();
assert_eq!(<$encoding>::encoded_len(vector.raw), vector.b64.len());
assert_eq!(vector.b64, &out[..]);
#[cfg(feature = "alloc")]
{
let out = <$encoding>::encode_string(vector.raw);
assert_eq!(vector.b64, &out[..]);
}
}
}
#[test]
fn decode_test_vectors() {
let mut buf = [0u8; 1024];
for vector in TEST_VECTORS {
let out = <$encoding>::decode(vector.b64, &mut buf).unwrap();
assert_eq!(vector.raw, &out[..]);
let n = vector.b64.len();
buf[..n].copy_from_slice(vector.b64.as_bytes());
let out = <$encoding>::decode_in_place(&mut buf[..n]).unwrap();
assert_eq!(vector.raw, out);
#[cfg(feature = "alloc")]
{
let out = <$encoding>::decode_vec(vector.b64).unwrap();
assert_eq!(vector.raw, &out[..]);
}
}
}
#[test]
fn encode_and_decode_various_lengths() {
let data = [b'X'; 64];
let mut inbuf = [0u8; 1024];
let mut outbuf = [0u8; 1024];
for i in 0..data.len() {
let encoded = <$encoding>::encode(&data[..i], &mut inbuf).unwrap();
// Make sure it round trips
let decoded = <$encoding>::decode(encoded, &mut outbuf).unwrap();
assert_eq!(decoded, &data[..i]);
let elen = <$encoding>::encode(&data[..i], &mut inbuf).unwrap().len();
let buf = &mut inbuf[..elen];
let decoded = <$encoding>::decode_in_place(buf).unwrap();
assert_eq!(decoded, &data[..i]);
#[cfg(feature = "alloc")]
{
let encoded = <$encoding>::encode_string(&data[..i]);
let decoded = <$encoding>::decode_vec(&encoded).unwrap();
assert_eq!(decoded, &data[..i]);
}
}
}
};
}

70
vendor/base64ct/tests/crypt.rs vendored Normal file
View File

@@ -0,0 +1,70 @@
//! `crypt(3)` Base64 tests
#![allow(deprecated)]
#[macro_use]
mod common;
use crate::common::*;
use base64ct::Base64Crypt;
const TEST_VECTORS: &[TestVector] = &[
TestVector { raw: b"", b64: "" },
TestVector {
raw: b"\0",
b64: "..",
},
TestVector {
raw: b"***",
b64: "8Wce",
},
TestVector {
raw: b"\x01\x02\x03\x04",
b64: ".E61/.",
},
TestVector {
raw: b"\xAD\xAD\xAD\xAD\xAD",
b64: "fOqhfOo",
},
TestVector {
raw: b"\xFF\xEF\xFE\xFF\xEF\xFE",
b64: "zyzyzyzy",
},
TestVector {
raw: b"\xFF\xFF\xFF\xFF\xFF",
b64: "zzzzzzw",
},
TestVector {
raw: b"\x40\xC1\x3F\xBD\x05\x4C\x72\x2A\xA3\xC2\xF2\x11\x73\xC0\x69\xEA\
\x49\x7D\x35\x29\x6B\xCC\x24\x65\xF6\xF9\xD0\x41\x08\x7B\xD7\xA9",
b64: "EA2zjEJAQWeXkj6FQw/duYZxBGZfn0FZxjbEEEVvpuY",
},
TestVector {
raw: b"\x00\x10\x83\x10Q\x87 \x92\x8B0\xD3\x8FA\x14\x93QU\x97a\x96\x9Bq\
\xD7\x9F\x82\x18\xA3\x92Y\xA7\xA2\x9A\xAB\xB2\xDB\xAF\xC3\x1C\xB3\
\xFB\xF0\x00",
b64: "./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnyz..",
},
];
impl_tests!(Base64Crypt);
#[test]
fn reject_trailing_whitespace() {
let input = "OKC9tOTKagohutGPa6/n4ij7LQjpxAPj7tlOOOf5z4i\n";
let mut buf = [0u8; 1024];
assert_eq!(
Base64Crypt::decode(input, &mut buf),
Err(Error::InvalidEncoding)
);
}
#[test]
fn unpadded_reject_trailing_equals() {
let input = "OKC9tOTKagohutGPa6/n4ij7LQjpxAPj7tlOOOf5z4i=";
let mut buf = [0u8; 1024];
assert_eq!(
Base64Crypt::decode(input, &mut buf),
Err(Error::InvalidEncoding)
);
}

78
vendor/base64ct/tests/pbkdf2.rs vendored Normal file
View File

@@ -0,0 +1,78 @@
//! PBKDF2 MCF variant of Base64
#[macro_use]
mod common;
use crate::common::*;
use base64ct::Base64Pbkdf2;
const TEST_VECTORS: &[TestVector] = &[
TestVector { raw: b"", b64: "" },
TestVector {
raw: b"\0",
b64: "AA",
},
TestVector {
raw: b"***",
b64: "Kioq",
},
TestVector {
raw: b"\x01\x02\x03\x04",
b64: "AQIDBA",
},
TestVector {
raw: b"\xAD\xAD\xAD\xAD\xAD",
b64: "ra2tra0",
},
TestVector {
raw: b"\xFF\xEF\xFE\xFF\xEF\xFE",
b64: "/./././.",
},
TestVector {
raw: b"\xFF\xFF\xFF\xFF\xFF",
b64: "//////8",
},
TestVector {
raw: b"\x40\xC1\x3F\xBD\x05\x4C\x72\x2A\xA3\xC2\xF2\x11\x73\xC0\x69\xEA\
\x49\x7D\x35\x29\x6B\xCC\x24\x65\xF6\xF9\xD0\x41\x08\x7B\xD7\xA9",
b64: "QME/vQVMciqjwvIRc8Bp6kl9NSlrzCRl9vnQQQh716k",
},
TestVector {
raw: b"\x00\x10\x83\x10Q\x87 \x92\x8B0\xD3\x8FA\x14\x93QU\x97a\x96\x9Bq\
\xD7\x9F\x82\x18\xA3\x92Y\xA7\xA2\x9A\xAB\xB2\xDB\xAF\xC3\x1C\xB3\
\xFB\xF0\x00",
b64: "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz./AA",
},
];
impl_tests!(Base64Pbkdf2);
#[test]
fn reject_trailing_whitespace() {
let input = "EA2zjEJAQWeXkj6FQw/duYZxBGZfn0FZxjbEEEVvpuY\n";
let mut buf = [0u8; 1024];
assert_eq!(
Base64Pbkdf2::decode(input, &mut buf),
Err(Error::InvalidEncoding)
);
}
#[test]
fn reject_trailing_equals() {
let input = "EA2zjEJAQWeXkj6FQw/duYZxBGZfn0FZxjbEEEVvpuY=";
let mut buf = [0u8; 1024];
assert_eq!(
Base64Pbkdf2::decode(input, &mut buf),
Err(Error::InvalidEncoding)
);
}
#[test]
fn reject_non_canonical_encoding() {
let input = "Mi";
let mut buf = [0u8; 8];
assert_eq!(
Base64Pbkdf2::decode(input, &mut buf),
Err(Error::InvalidEncoding)
);
}

View File

@@ -0,0 +1,10 @@
# Seeds for failure cases proptest has generated in the past. It is
# automatically read and these particular cases re-run before any
# novel cases are generated.
#
# It is recommended to check this file in to source control so that
# everyone who runs the test benefits from these saved cases.
cc ea4af6a6a3c5feddd17be51d3bb3d863881547acf50b553e76da3f34f8b755d4 # shrinks to base64ish = ""
cc 348d4acf2c3d1e8db3772f5645179e24b50178747469da9709e60800175eef80 # shrinks to bytes = [240, 144, 128, 128, 240, 144, 128, 128, 32, 32, 32, 194, 161, 48, 97, 97, 65, 194, 161, 32, 97, 194, 161, 32, 240, 144, 128, 128, 194, 161, 48, 32, 97, 194, 161, 240, 144, 128, 128, 32, 224, 160, 128, 97, 224, 160, 128, 48, 48, 194, 161, 32, 240, 144, 128, 128, 11, 65, 97, 48, 65, 65, 97, 11, 240, 144, 128, 128, 240, 144, 128, 128, 48, 224, 160, 128, 194, 161, 32, 32, 194, 161, 32, 48, 97, 240, 144, 128, 128, 224, 160, 128, 240, 144, 128, 128, 0, 224, 160, 128, 32, 240, 144, 128, 128, 0, 32, 32, 97, 240, 144, 128, 128, 240, 144, 128, 128, 240, 144, 128, 128, 240, 144, 128, 128, 0, 0, 240, 144, 128, 128, 32, 240, 144, 128, 128, 32, 48, 65, 11, 32, 65, 48, 48, 65, 65, 194, 161, 32, 224, 160, 128, 240, 144, 128, 128, 224, 160, 128, 0, 65, 0, 65, 32, 194, 161, 240, 144, 128, 128, 32, 65, 32, 0, 97, 32, 97, 11, 11, 48, 97, 97, 240, 144, 128, 128, 65, 240, 144, 128, 128, 194, 161], line_width = 10, chunk_size = 163
cc 0c0ee7f6a60d24431333f5c39c506b818a6c21022e39288619c8f78f29d30b1c # shrinks to bytes = [240, 144, 128, 128, 194, 161, 194, 161, 240, 144, 128, 128, 194, 161, 240, 144, 128, 128, 65, 224, 160, 128, 97, 224, 160, 128, 32, 97, 32, 65, 224, 160, 128, 0, 97, 0, 240, 144, 128, 128, 97, 194, 161, 32, 240, 144, 128, 128, 11, 48, 32, 65, 32, 240, 144, 128, 128, 97, 194, 161, 48, 48, 240, 144, 128, 128, 194, 161, 194, 161, 32, 194, 161, 48, 0, 32, 48, 224, 160, 128, 65, 240, 144, 128, 128, 11, 65, 11, 240, 144, 128, 128, 32, 32, 194, 161, 240, 144, 128, 128, 224, 160, 128, 240, 144, 128, 128, 194, 161, 224, 160, 128, 65, 32, 240, 144, 128, 128, 32, 240, 144, 128, 128, 48, 240, 144, 128, 128, 0, 48, 240, 144, 128, 128, 48, 65, 65, 11, 0, 65, 240, 144, 128, 128, 240, 144, 128, 128, 32, 65, 240, 144, 128, 128, 112, 75, 46, 232, 143, 132, 240, 159, 149, 180, 101, 92, 11, 42, 98, 244, 142, 150, 136, 83, 13, 243, 189, 168, 131, 194, 154, 9, 243, 129, 165, 130, 241, 138, 188, 150, 39, 241, 170, 133, 154, 39, 61, 244, 136, 146, 157, 46, 91, 108, 34, 66, 0, 239, 187, 191, 34, 240, 158, 187, 152, 241, 187, 172, 188, 46, 239, 191, 189, 244, 143, 139, 131, 13, 13, 226, 128, 174, 60, 200, 186, 194, 151, 27, 105, 43, 226, 128, 174, 70, 0, 38, 127, 194, 133, 195, 177, 123, 127, 121, 241, 128, 141, 141, 244, 137, 146, 189, 55, 54, 9, 240, 159, 149, 180, 2, 209, 168, 239, 187, 191, 11, 34, 123, 32, 42, 242, 171, 149, 149, 102, 241, 174, 190, 188, 242, 144, 186, 145, 1, 84, 34, 56, 7, 0, 194, 188, 43, 117, 48, 96, 11, 60, 242, 190, 170, 187, 47, 99, 37, 241, 175, 142, 186, 240, 178, 162, 136, 46, 2, 241, 176, 162, 162, 37, 242, 148, 135, 179, 11, 36, 104, 244, 130, 136, 177], line_width = 24, chunk_size = 240
cc b6d81102accbff17f00786b06c6040fc59fee8aa087033c9b5604d2a3f246afd # shrinks to bytes = [32, 65, 11, 97, 97, 32, 240, 144, 128, 128, 97, 32, 65, 0, 0, 32, 240, 144, 128, 128, 97, 65, 97, 97, 240, 144, 128, 128, 240, 144, 128, 128, 65, 48, 240, 144, 128, 128, 240, 144, 128, 128, 32, 0, 97, 97, 240, 144, 128, 128, 65, 32, 194, 161, 65, 0, 32, 11, 97, 32, 32, 11, 32, 240, 144, 128, 128, 240, 144, 128, 128, 194, 128, 32, 48, 65, 32, 240, 144, 128, 128, 240, 144, 128, 128, 240, 144, 128, 128, 194, 161, 32, 194, 161, 48, 224, 160, 128, 240, 144, 128, 128, 97, 32, 0, 48, 240, 144, 128, 128, 0, 11, 240, 144, 128, 128, 97, 240, 144, 128, 128, 11, 32, 0, 32, 0, 194, 161, 194, 161, 56, 242, 150, 180, 168, 243, 187, 153, 181, 46, 36, 121, 70, 8, 226, 128, 174, 242, 135, 172, 189, 0, 194, 169, 244, 130, 145, 146, 240, 159, 149, 180, 63, 240, 184, 155, 139, 27, 243, 185, 138, 139, 194, 162, 46, 242, 148, 129, 171, 195, 143, 56, 241, 147, 151, 173, 240, 159, 149, 180, 33, 89, 36, 37, 240, 159, 149, 180, 200, 186, 117, 194, 165, 77, 241, 171, 180, 143, 60, 96, 242, 175, 134, 177, 27, 1, 42, 242, 145, 189, 151, 92, 39, 96, 38, 243, 181, 148, 171, 243, 164, 185, 188, 47, 195, 181, 0, 226, 128, 174, 13, 233, 136, 141, 57, 200, 186, 243, 129, 145, 159, 242, 137, 177, 176, 122, 61, 243, 140, 180, 151, 239, 191, 189, 80, 194, 144, 121, 42, 239, 191, 189, 231, 173, 145, 75, 91, 0, 123, 238, 154, 139, 58, 240, 179, 187, 172, 107, 13, 13, 123, 241, 152, 132, 160, 242, 130, 149, 190, 92, 239, 187, 191, 117, 241, 182, 130, 165, 241, 165, 155, 168, 39, 60, 0, 0, 13, 200, 186, 83, 37, 243, 174, 183, 166, 11, 0, 237, 134, 157, 39, 58, 113, 44, 243, 135, 142, 174, 9, 9, 195, 184, 74, 241, 146, 132, 133, 34, 58, 92, 123, 239, 187, 191, 37, 58, 239, 187, 191, 77, 9, 243, 183, 143, 189, 243, 159, 143, 171, 243, 162, 128, 179, 241, 137, 158, 163, 127, 60, 195, 159, 106, 47, 242, 135, 154, 161, 51, 243, 160, 136, 149, 91, 241, 175, 181, 149, 96, 58, 46, 11, 37, 107, 32, 52, 237, 136, 144, 77, 194, 156, 42, 13, 39, 61, 2, 59, 48, 58, 240, 159, 149, 180, 4, 96, 127, 230, 166, 145, 58, 239, 187, 191, 242, 135, 132, 146, 241, 178, 129, 185, 36], line_width = 118, chunk_size = 147

170
vendor/base64ct/tests/proptests.rs vendored Normal file
View File

@@ -0,0 +1,170 @@
//! Equivalence tests between `base64` crate and `base64ct`.
#![cfg(feature = "std")]
// TODO(tarcieri): fix `base64` crate deprecations
// warning: use of deprecated function `base64::encode`: Use Engine::encode
#![allow(deprecated)]
use base64ct::{Base64 as Base64ct, Base64Unpadded as Base64UnpaddedCt, Encoding};
use proptest::{prelude::*, string::*};
/// Incremental Base64 decoder.
type Decoder<'a> = base64ct::Decoder<'a, Base64ct>;
/// Incremental Base64 encoder.
type Encoder<'a> = base64ct::Encoder<'a, Base64ct>;
proptest! {
/// Ensure `base64ct` decodes data encoded by `base64` ref crate
#[test]
fn decode_equiv(bytes in bytes_regex(".{0,256}").unwrap()) {
let encoded = base64::encode(&bytes);
let decoded = Base64ct::decode_vec(&encoded);
prop_assert_eq!(Ok(bytes), decoded);
}
/// Ensure that `base64ct`'s incremental decoder is able to decode randomly
/// generated inputs encoded by the `base64` ref crate
#[test]
fn decode_incremental(bytes in bytes_regex(".{1,256}").unwrap(), chunk_size in 1..256usize) {
let encoded = base64::encode(&bytes);
let chunk_size = match chunk_size % bytes.len() {
0 => 1,
n => n
};
let mut buffer = [0u8; 384];
let mut decoder = Decoder::new(encoded.as_bytes()).unwrap();
let mut remaining_len = decoder.remaining_len();
for chunk in bytes.chunks(chunk_size) {
prop_assert!(!decoder.is_finished());
let decoded = decoder.decode(&mut buffer[..chunk.len()]);
prop_assert_eq!(Ok(chunk), decoded);
remaining_len -= decoded.unwrap().len();
prop_assert_eq!(remaining_len, decoder.remaining_len());
}
prop_assert!(decoder.is_finished());
prop_assert_eq!(decoder.remaining_len(), 0);
}
#[test]
fn decode_incremental_wrapped(
bytes in bytes_regex(".{1,256}").unwrap(),
line_width in 4..128usize,
chunk_size in 1..256usize
) {
for line_ending in ["\r", "\n", "\r\n"] {
let encoded = base64::encode(&bytes);
let mut encoded_wrapped = Vec::new();
let mut lines = encoded.as_bytes().chunks_exact(line_width);
for line in &mut lines {
encoded_wrapped.extend_from_slice(line);
encoded_wrapped.extend_from_slice(line_ending.as_bytes());
}
let last = lines.remainder();
if last.is_empty() {
encoded_wrapped.truncate(encoded_wrapped.len() - line_ending.len());
} else {
encoded_wrapped.extend_from_slice(last);
}
let chunk_size = match chunk_size % bytes.len() {
0 => 1,
n => n
};
let mut buffer = [0u8; 384];
let mut decoder = Decoder::new_wrapped(&encoded_wrapped, line_width).unwrap();
let mut remaining_len = decoder.remaining_len();
for chunk in bytes.chunks(chunk_size) {
prop_assert!(!decoder.is_finished());
let decoded = decoder.decode(&mut buffer[..chunk.len()]);
prop_assert_eq!(Ok(chunk), decoded);
remaining_len -= decoded.unwrap().len();
prop_assert_eq!(remaining_len, decoder.remaining_len());
}
prop_assert!(decoder.is_finished());
prop_assert_eq!(decoder.remaining_len(), 0);
}
}
/// Ensure `base64ct` and `base64` ref crate decode randomly generated
/// inputs equivalently.
///
/// Inputs are selected to be valid characters in the standard Base64
/// padded alphabet, but are not necessarily valid Base64.
#[test]
fn decode_random(base64ish in string_regex("[A-Za-z0-9+/]{0,256}").unwrap()) {
let base64ish_padded = match base64ish.len() % 4 {
0 => base64ish,
n => {
let padding_len = 4 - n;
base64ish + &"=".repeat(padding_len)
}
};
let decoded_ct = Base64ct::decode_vec(&base64ish_padded).ok();
let decoded_ref = base64::decode(&base64ish_padded).ok();
prop_assert_eq!(decoded_ct, decoded_ref);
}
/// Ensure `base64ct` and the `base64` ref crate encode randomly generated
/// inputs equivalently.
#[test]
fn encode_equiv(bytes in bytes_regex(".{0,256}").unwrap()) {
let encoded_ct = Base64ct::encode_string(&bytes);
let encoded_ref = base64::encode(&bytes);
prop_assert_eq!(encoded_ct, encoded_ref);
}
/// Ensure that `base64ct`'s incremental encoder is able to encode randomly
/// generated inputs which match what's encoded by the `base64` ref crate
#[test]
fn encode_incremental(bytes in bytes_regex(".{1,256}").unwrap(), chunk_size in 1..256usize) {
let expected = base64::encode(&bytes);
let chunk_size = match chunk_size % bytes.len() {
0 => 1,
n => n
};
let mut buffer = [0u8; 1024];
let mut encoder = Encoder::new(&mut buffer).unwrap();
for chunk in bytes.chunks(chunk_size) {
encoder.encode(chunk).unwrap();
}
prop_assert_eq!(expected, encoder.finish().unwrap());
}
/// Make sure that base64ct and base64 both decode the same values
/// when expecting padded inputs, and produce the same outputs for those values.
#[test]
fn decode_arbitrary_padded(string in string_regex("[a-zA-Z0-9/+=?]{0,256}").unwrap()) {
let actual = Base64ct::decode_vec(&string);
let expected = base64::decode( &string);
assert_eq!(actual.ok(), expected.ok());
}
/// Make sure that base64ct and base64 both decode the same values
/// when expecting unpadded inputs, and produce the same outputs for those values.
#[test]
fn decode_arbitrary_unpadded(string in string_regex("[a-zA-Z0-9/+=?]{0,256}").unwrap()) {
use base64::{engine::general_purpose::STANDARD_NO_PAD, Engine as _};
let actual = Base64UnpaddedCt::decode_vec(&string);
let expected = STANDARD_NO_PAD.decode(&string);
assert_eq!(actual.ok(), expected.ok());
}
}

98
vendor/base64ct/tests/shacrypt.rs vendored Normal file
View File

@@ -0,0 +1,98 @@
//! `crypt(3)` Base64 tests
#[macro_use]
mod common;
use crate::common::*;
use base64ct::Base64ShaCrypt;
const TEST_VECTORS: &[TestVector] = &[
TestVector { raw: b"", b64: "" },
TestVector {
raw: b"\x55",
b64: "J/",
},
TestVector {
raw: b"\x55\xaa",
b64: "Jd8",
},
TestVector {
raw: b"\x55\xaa\x55",
b64: "JdOJ",
},
TestVector {
raw: b"\x55\xaa\x55\xaa",
b64: "JdOJe0",
},
TestVector {
raw: b"\x55\xaa\x55\xaa\x55",
b64: "JdOJeK3",
},
TestVector {
raw: b"\x55\xaa\x55\xaa\x55\xaa",
b64: "JdOJeKZe",
},
TestVector {
raw: b"\x55\xaa\x55\xaf",
b64: "JdOJj0",
},
TestVector {
raw: b"\x55\xaa\x55\xaa\x5f",
b64: "JdOJey3",
},
TestVector {
raw: b"\0",
b64: "..",
},
TestVector {
raw: b"***",
b64: "ecW8",
},
TestVector {
raw: b"\x01\x02\x03\x04",
b64: "/6k.2.",
},
TestVector {
raw: b"\xAD\xAD\xAD\xAD\xAD",
b64: "hqOfhq8",
},
TestVector {
raw: b"\xFF\xEF\xFE\xFF\xEF\xFE",
b64: "zzizzziz",
},
TestVector {
raw: b"\xFF\xFF\xFF\xFF\xFF",
b64: "zzzzzzD",
},
TestVector {
raw: b"\x40\xC1\x3F\xBD\x05\x4C\x72\x2A\xA3\xC2\xF2\x11\x73\xC0\x69\xEA\
\x49\x7D\x35\x29\x6B\xCC\x24\x65\xF6\xF9\xD0\x41\x08\x7B\xD7\xA9",
b64: ".3wDxK.Hmdmc09T2n/QOebITpYmOAHGNqbDo/VkSLb8",
},
TestVector {
raw: b"@ \x0cDa\x1cH\xa2,L\xe3<P$MTe]X\xa6m\\\xe7}`(\x8edi\x9eh\xaa\xael\xeb\xbep,\xcf\xfe\x0f\x00",
b64: "./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnyz..",
},
];
impl_tests!(Base64ShaCrypt);
#[test]
fn reject_trailing_whitespace() {
let input = "OKC9tOTKagohutGPa6/n4ij7LQjpxAPj7tlOOOf5z4i\n";
let mut buf = [0u8; 1024];
assert_eq!(
Base64ShaCrypt::decode(input, &mut buf),
Err(Error::InvalidEncoding)
);
}
#[test]
fn unpadded_reject_trailing_equals() {
let input = "OKC9tOTKagohutGPa6/n4ij7LQjpxAPj7tlOOOf5z4i=";
let mut buf = [0u8; 1024];
assert_eq!(
Base64ShaCrypt::decode(input, &mut buf),
Err(Error::InvalidEncoding)
);
}

142
vendor/base64ct/tests/standard.rs vendored Normal file
View File

@@ -0,0 +1,142 @@
//! Standard Base64 tests
#[macro_use]
mod common;
/// Standard Base64 with `=` padding
mod padded {
use crate::common::*;
use base64ct::Base64;
const TEST_VECTORS: &[TestVector] = &[
TestVector { raw: b"", b64: "" },
TestVector {
raw: b"\0",
b64: "AA==",
},
TestVector {
raw: b"***",
b64: "Kioq",
},
TestVector {
raw: b"\x01\x02\x03\x04",
b64: "AQIDBA==",
},
TestVector {
raw: b"\xAD\xAD\xAD\xAD\xAD",
b64: "ra2tra0=",
},
TestVector {
raw: b"\xFF\xEF\xFE\xFF\xEF\xFE",
b64: "/+/+/+/+",
},
TestVector {
raw: b"\xFF\xFF\xFF\xFF\xFF",
b64: "//////8=",
},
TestVector {
raw: b"\x40\xC1\x3F\xBD\x05\x4C\x72\x2A\xA3\xC2\xF2\x11\x73\xC0\x69\xEA\
\x49\x7D\x35\x29\x6B\xCC\x24\x65\xF6\xF9\xD0\x41\x08\x7B\xD7\xA9",
b64: "QME/vQVMciqjwvIRc8Bp6kl9NSlrzCRl9vnQQQh716k=",
},
TestVector {
raw: b"\x00\x10\x83\x10Q\x87 \x92\x8B0\xD3\x8FA\x14\x93QU\x97a\x96\x9Bq\
\xD7\x9F\x82\x18\xA3\x92Y\xA7\xA2\x9A\xAB\xB2\xDB\xAF\xC3\x1C\xB3\
\xFB\xF0\x00",
b64: "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz+/AA",
},
];
impl_tests!(Base64);
#[test]
fn reject_trailing_whitespace() {
let input = "QME/vQVMciqjwvIRc8Bp6kl9NSlrzCRl9vnQQQh716k\n";
let mut buf = [0u8; 1024];
assert_eq!(Base64::decode(input, &mut buf), Err(Error::InvalidEncoding));
}
#[test]
fn reject_invalid_padding() {
let input = "AA/=";
let mut buf = [0u8; 1024];
assert_eq!(Base64::decode(input, &mut buf), Err(Error::InvalidEncoding));
}
}
/// Standard Base64 *without* padding
mod unpadded {
use crate::common::*;
use base64ct::Base64Unpadded;
const TEST_VECTORS: &[TestVector] = &[
TestVector { raw: b"", b64: "" },
TestVector {
raw: b"\0",
b64: "AA",
},
TestVector {
raw: b"***",
b64: "Kioq",
},
TestVector {
raw: b"\x01\x02\x03\x04",
b64: "AQIDBA",
},
TestVector {
raw: b"\xAD\xAD\xAD\xAD\xAD",
b64: "ra2tra0",
},
TestVector {
raw: b"\xFF\xEF\xFE\xFF\xEF\xFE",
b64: "/+/+/+/+",
},
TestVector {
raw: b"\xFF\xFF\xFF\xFF\xFF",
b64: "//////8",
},
TestVector {
raw: b"\x40\xC1\x3F\xBD\x05\x4C\x72\x2A\xA3\xC2\xF2\x11\x73\xC0\x69\xEA\
\x49\x7D\x35\x29\x6B\xCC\x24\x65\xF6\xF9\xD0\x41\x08\x7B\xD7\xA9",
b64: "QME/vQVMciqjwvIRc8Bp6kl9NSlrzCRl9vnQQQh716k",
},
TestVector {
raw: b"\x00\x10\x83\x10Q\x87 \x92\x8B0\xD3\x8FA\x14\x93QU\x97a\x96\x9Bq\
\xD7\x9F\x82\x18\xA3\x92Y\xA7\xA2\x9A\xAB\xB2\xDB\xAF\xC3\x1C\xB3\
\xFB\xF0\x00",
b64: "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz+/AA",
},
];
impl_tests!(Base64Unpadded);
#[test]
fn reject_trailing_whitespace() {
let input = "EA2zjEJAQWeXkj6FQw/duYZxBGZfn0FZxjbEEEVvpuY\n";
let mut buf = [0u8; 1024];
assert_eq!(
Base64Unpadded::decode(input, &mut buf),
Err(Error::InvalidEncoding)
);
}
#[test]
fn unpadded_reject_trailing_equals() {
let input = "EA2zjEJAQWeXkj6FQw/duYZxBGZfn0FZxjbEEEVvpuY=";
let mut buf = [0u8; 1024];
assert_eq!(
Base64Unpadded::decode(input, &mut buf),
Err(Error::InvalidEncoding)
);
}
#[test]
fn reject_non_canonical_encoding() {
let input = "Mi";
let mut buf = [0u8; 8];
assert_eq!(
Base64Unpadded::decode(input, &mut buf),
Err(Error::InvalidEncoding)
);
}
}

128
vendor/base64ct/tests/url.rs vendored Normal file
View File

@@ -0,0 +1,128 @@
//! URL-safe Base64 tests
#[macro_use]
mod common;
/// URL-safe Base64 with `=` padding
mod padded {
use crate::common::*;
use base64ct::Base64Url;
const TEST_VECTORS: &[TestVector] = &[
TestVector { raw: b"", b64: "" },
TestVector {
raw: b"\0",
b64: "AA==",
},
TestVector {
raw: b"***",
b64: "Kioq",
},
TestVector {
raw: b"\x01\x02\x03\x04",
b64: "AQIDBA==",
},
TestVector {
raw: b"\xAD\xAD\xAD\xAD\xAD",
b64: "ra2tra0=",
},
TestVector {
raw: b"\xFF\xEF\xFE\xFF\xEF\xFE",
b64: "_-_-_-_-",
},
TestVector {
raw: b"\xFF\xFF\xFF\xFF\xFF",
b64: "______8=",
},
TestVector {
raw: b"\x40\xC1\x3F\xBD\x05\x4C\x72\x2A\xA3\xC2\xF2\x11\x73\xC0\x69\xEA\
\x49\x7D\x35\x29\x6B\xCC\x24\x65\xF6\xF9\xD0\x41\x08\x7B\xD7\xA9",
b64: "QME_vQVMciqjwvIRc8Bp6kl9NSlrzCRl9vnQQQh716k=",
},
TestVector {
raw: b"\x00\x10\x83\x10Q\x87 \x92\x8B0\xD3\x8FA\x14\x93QU\x97a\x96\x9Bq\
\xD7\x9F\x82\x18\xA3\x92Y\xA7\xA2\x9A\xAB\xB2\xDB\xAF\xC3\x1C\xB3\
\xFB\xF0\x00",
b64: "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz-_AA",
},
];
impl_tests!(Base64Url);
#[test]
fn reject_trailing_whitespace() {
let input = "QME/vQVMciqjwvIRc8Bp6kl9NSlrzCRl9vnQQQh716k\n";
let mut buf = [0u8; 1024];
assert_eq!(
Base64Url::decode(input, &mut buf),
Err(Error::InvalidEncoding)
);
}
}
/// URL-safe Base64 *without* padding
mod unpadded {
use crate::common::*;
use base64ct::Base64UrlUnpadded;
const TEST_VECTORS: &[TestVector] = &[
TestVector { raw: b"", b64: "" },
TestVector {
raw: b"\0",
b64: "AA",
},
TestVector {
raw: b"***",
b64: "Kioq",
},
TestVector {
raw: b"\x01\x02\x03\x04",
b64: "AQIDBA",
},
TestVector {
raw: b"\xAD\xAD\xAD\xAD\xAD",
b64: "ra2tra0",
},
TestVector {
raw: b"\xFF\xEF\xFE\xFF\xEF\xFE",
b64: "_-_-_-_-",
},
TestVector {
raw: b"\xFF\xFF\xFF\xFF\xFF",
b64: "______8",
},
TestVector {
raw: b"\x40\xC1\x3F\xBD\x05\x4C\x72\x2A\xA3\xC2\xF2\x11\x73\xC0\x69\xEA\
\x49\x7D\x35\x29\x6B\xCC\x24\x65\xF6\xF9\xD0\x41\x08\x7B\xD7\xA9",
b64: "QME_vQVMciqjwvIRc8Bp6kl9NSlrzCRl9vnQQQh716k",
},
TestVector {
raw: b"\x00\x10\x83\x10Q\x87 \x92\x8B0\xD3\x8FA\x14\x93QU\x97a\x96\x9Bq\
\xD7\x9F\x82\x18\xA3\x92Y\xA7\xA2\x9A\xAB\xB2\xDB\xAF\xC3\x1C\xB3\
\xFB\xF0\x00",
b64: "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz-_AA",
},
];
impl_tests!(Base64UrlUnpadded);
#[test]
fn reject_trailing_whitespace() {
let input = "QME/vQVMciqjwvIRc8Bp6kl9NSlrzCRl9vnQQQh716k\n";
let mut buf = [0u8; 1024];
assert_eq!(
Base64UrlUnpadded::decode(input, &mut buf),
Err(Error::InvalidEncoding)
);
}
#[test]
fn unpadded_reject_trailing_equals() {
let input = "QME_vQVMciqjwvIRc8Bp6kl9NSlrzCRl9vnQQQh716k=";
let mut buf = [0u8; 1024];
assert_eq!(
Base64UrlUnpadded::decode(input, &mut buf),
Err(Error::InvalidEncoding)
);
}
}