Add Oblivious HTTP (OHTTP) client for iOS
Using the ohttp-rs and bhttp-rs Rust libraries to perform underlying encoding and encryption. This is exposed to Swift using URLRequest/Response types that are typical for the platform. Note that only iOS is targetted since Gecko projects have a builtin OHTTP solution within Necko. This use currently using the Rust Crypto libraries for underlying HPKE support, but this should be changed to NSS or OpenSSL in future.
This commit is contained in:
Родитель
daa91aa15e
Коммит
14def5e1e1
|
@ -2,6 +2,12 @@
|
|||
|
||||
[Full Changelog](In progress)
|
||||
|
||||
## General
|
||||
|
||||
### ✨ What's New ✨
|
||||
|
||||
- Added an OHTTP client library for iOS based on `ohttp` Rust crate ([#5749](https://github.com/mozilla/application-services/pull/5749)). This allows iOS products to use the same OHTTP libraries as Gecko-based products.
|
||||
|
||||
# v117.0 (_2023-07-31_)
|
||||
|
||||
## General
|
||||
|
|
|
@ -17,6 +17,42 @@ version = "1.0.2"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe"
|
||||
|
||||
[[package]]
|
||||
name = "aead"
|
||||
version = "0.4.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0b613b8e1e3cf911a086f53f03bf286f52fd7a7258e4fa606f0ef220d39d8877"
|
||||
dependencies = [
|
||||
"generic-array",
|
||||
"rand_core 0.6.3",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "aes"
|
||||
version = "0.7.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9e8b47f52ea9bae42228d07ec09eb676433d7c4ed1ebdf0f1d1c29ed446f1ab8"
|
||||
dependencies = [
|
||||
"cfg-if 1.0.0",
|
||||
"cipher",
|
||||
"cpufeatures 0.2.2",
|
||||
"opaque-debug",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "aes-gcm"
|
||||
version = "0.9.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "df5f85a83a7d8b0442b6aa7b504b8212c1733da07b98aae43d4bc21b2cb3cdf6"
|
||||
dependencies = [
|
||||
"aead",
|
||||
"aes",
|
||||
"cipher",
|
||||
"ctr",
|
||||
"ghash",
|
||||
"subtle",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ahash"
|
||||
version = "0.7.6"
|
||||
|
@ -138,6 +174,17 @@ version = "0.5.2"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "23b62fc65de8e4e7f52534fb52b0f3ed04746ae267519eef2a83941e8085068b"
|
||||
|
||||
[[package]]
|
||||
name = "as-ohttp-client"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"bhttp",
|
||||
"ohttp",
|
||||
"parking_lot",
|
||||
"thiserror",
|
||||
"uniffi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "askama"
|
||||
version = "0.10.5"
|
||||
|
@ -324,6 +371,15 @@ dependencies = [
|
|||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bhttp"
|
||||
version = "0.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1300dca7a20730cce82c33fbf8795862556645ef5e9ee835390278c3fe1eb1d0"
|
||||
dependencies = [
|
||||
"thiserror",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bincode"
|
||||
version = "1.3.3"
|
||||
|
@ -498,6 +554,31 @@ version = "1.0.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||
|
||||
[[package]]
|
||||
name = "chacha20"
|
||||
version = "0.7.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fee7ad89dc1128635074c268ee661f90c3f7e83d9fd12910608c36b47d6c3412"
|
||||
dependencies = [
|
||||
"cfg-if 1.0.0",
|
||||
"cipher",
|
||||
"cpufeatures 0.1.5",
|
||||
"zeroize",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "chacha20poly1305"
|
||||
version = "0.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1580317203210c517b6d44794abfbe600698276db18127e37ad3e69bf5e848e5"
|
||||
dependencies = [
|
||||
"aead",
|
||||
"chacha20",
|
||||
"cipher",
|
||||
"poly1305",
|
||||
"zeroize",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "chrono"
|
||||
version = "0.4.26"
|
||||
|
@ -514,6 +595,15 @@ dependencies = [
|
|||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cipher"
|
||||
version = "0.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7ee52072ec15386f770805afd189a01c8841be8696bed250fa2f13c4c0d6dfb7"
|
||||
dependencies = [
|
||||
"generic-array",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cirrus"
|
||||
version = "0.1.0"
|
||||
|
@ -676,6 +766,15 @@ version = "0.8.3"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5827cebf4670468b8772dd191856768aedcb1b0278a04f989f7766351917b9dc"
|
||||
|
||||
[[package]]
|
||||
name = "cpufeatures"
|
||||
version = "0.1.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "66c99696f6c9dd7f35d486b9d04d7e6e202aa3e8c40d553f2fdf5e7e0c6a71ef"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cpufeatures"
|
||||
version = "0.2.2"
|
||||
|
@ -784,6 +883,16 @@ dependencies = [
|
|||
"once_cell",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crypto-mac"
|
||||
version = "0.11.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b1d1a86f49236c215f271d40892d5fc950490551400b02ef360692c29815c714"
|
||||
dependencies = [
|
||||
"generic-array",
|
||||
"subtle",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "csv"
|
||||
version = "1.1.6"
|
||||
|
@ -827,6 +936,15 @@ dependencies = [
|
|||
"syn 1.0.98",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ctr"
|
||||
version = "0.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "049bb91fb4aaf0e3c7efa6cd5ef877dbbbd15b39dad06d9948de4ec8a75761ea"
|
||||
dependencies = [
|
||||
"cipher",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ctrlc"
|
||||
version = "3.2.2"
|
||||
|
@ -837,6 +955,19 @@ dependencies = [
|
|||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "curve25519-dalek"
|
||||
version = "3.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0b9fdf9972b2bd6af2d913799d9ebc165ea4d2e65878e329d9c6b372c4491b61"
|
||||
dependencies = [
|
||||
"byteorder",
|
||||
"digest",
|
||||
"rand_core 0.5.1",
|
||||
"subtle",
|
||||
"zeroize",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cxx"
|
||||
version = "1.0.92"
|
||||
|
@ -1557,6 +1688,16 @@ dependencies = [
|
|||
"wasi 0.11.0+wasi-snapshot-preview1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ghash"
|
||||
version = "0.4.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1583cc1656d7839fd3732b80cf4f38850336cdb9b8ded1cd399ca62958de3c99"
|
||||
dependencies = [
|
||||
"opaque-debug",
|
||||
"polyval",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "gimli"
|
||||
version = "0.26.1"
|
||||
|
@ -1680,6 +1821,46 @@ version = "0.4.3"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70"
|
||||
|
||||
[[package]]
|
||||
name = "hkdf"
|
||||
version = "0.11.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "01706d578d5c281058480e673ae4086a9f4710d8df1ad80a5b03e39ece5f886b"
|
||||
dependencies = [
|
||||
"digest",
|
||||
"hmac",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hmac"
|
||||
version = "0.11.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2a2a2320eb7ec0ebe8da8f744d7812d9fc4cb4d09344ac01898dbcb6a20ae69b"
|
||||
dependencies = [
|
||||
"crypto-mac",
|
||||
"digest",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hpke"
|
||||
version = "0.7.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2b27779b5c326e3afe887e806ab04ac34922a9a723ee3fae62170b3f7ad33380"
|
||||
dependencies = [
|
||||
"aead",
|
||||
"aes-gcm",
|
||||
"byteorder",
|
||||
"chacha20poly1305",
|
||||
"digest",
|
||||
"generic-array",
|
||||
"hkdf",
|
||||
"rand_core 0.6.3",
|
||||
"sha2",
|
||||
"subtle",
|
||||
"x25519-dalek",
|
||||
"zeroize",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "http"
|
||||
version = "0.2.8"
|
||||
|
@ -2180,6 +2361,7 @@ dependencies = [
|
|||
name = "megazord_ios"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"as-ohttp-client",
|
||||
"autofill",
|
||||
"crashtest",
|
||||
"error-support",
|
||||
|
@ -2677,6 +2859,29 @@ dependencies = [
|
|||
"memchr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ohttp"
|
||||
version = "0.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "850ce328ec7e4dc1a9446c56aef700d21d914268c8529b96017a2bf10f74b70f"
|
||||
dependencies = [
|
||||
"aead",
|
||||
"aes-gcm",
|
||||
"byteorder",
|
||||
"chacha20poly1305",
|
||||
"hex",
|
||||
"hkdf",
|
||||
"hpke",
|
||||
"lazy_static",
|
||||
"log",
|
||||
"rand",
|
||||
"serde",
|
||||
"serde_derive",
|
||||
"sha2",
|
||||
"thiserror",
|
||||
"toml",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "once_cell"
|
||||
version = "1.13.0"
|
||||
|
@ -2934,6 +3139,29 @@ dependencies = [
|
|||
"plotters-backend",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "poly1305"
|
||||
version = "0.7.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "048aeb476be11a4b6ca432ca569e375810de9294ae78f4774e78ea98a9246ede"
|
||||
dependencies = [
|
||||
"cpufeatures 0.2.2",
|
||||
"opaque-debug",
|
||||
"universal-hash",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "polyval"
|
||||
version = "0.5.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8419d2b623c7c0896ff2d5d96e2cb4ede590fed28fcc34934f4c33c036e620a1"
|
||||
dependencies = [
|
||||
"cfg-if 1.0.0",
|
||||
"cpufeatures 0.2.2",
|
||||
"opaque-debug",
|
||||
"universal-hash",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ppv-lite86"
|
||||
version = "0.2.16"
|
||||
|
@ -3160,7 +3388,7 @@ checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404"
|
|||
dependencies = [
|
||||
"libc",
|
||||
"rand_chacha",
|
||||
"rand_core",
|
||||
"rand_core 0.6.3",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -3170,9 +3398,15 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88"
|
||||
dependencies = [
|
||||
"ppv-lite86",
|
||||
"rand_core",
|
||||
"rand_core 0.6.3",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand_core"
|
||||
version = "0.5.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19"
|
||||
|
||||
[[package]]
|
||||
name = "rand_core"
|
||||
version = "0.6.3"
|
||||
|
@ -3187,7 +3421,7 @@ name = "rand_rccrypto"
|
|||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"rand",
|
||||
"rand_core",
|
||||
"rand_core 0.6.3",
|
||||
"rc_crypto",
|
||||
]
|
||||
|
||||
|
@ -3678,7 +3912,7 @@ checksum = "4d58a1e1bf39749807d89cf2d98ac2dfa0ff1cb3faa38fbb64dd88ac8013d800"
|
|||
dependencies = [
|
||||
"block-buffer",
|
||||
"cfg-if 1.0.0",
|
||||
"cpufeatures",
|
||||
"cpufeatures 0.2.2",
|
||||
"digest",
|
||||
"opaque-debug",
|
||||
]
|
||||
|
@ -3815,6 +4049,12 @@ dependencies = [
|
|||
"syn 1.0.98",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "subtle"
|
||||
version = "2.4.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6bdef32e8150c2a081110b42772ffe7d7c9032b606bc226c8260fd97e0976601"
|
||||
|
||||
[[package]]
|
||||
name = "suggest"
|
||||
version = "0.1.0"
|
||||
|
@ -4403,6 +4643,16 @@ dependencies = [
|
|||
"serde_json",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "universal-hash"
|
||||
version = "0.4.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9f214e8f697e925001e66ec2c6e37a4ef93f0f78c2eed7814394e10c62025b05"
|
||||
dependencies = [
|
||||
"generic-array",
|
||||
"subtle",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "unsafe-libyaml"
|
||||
version = "0.2.8"
|
||||
|
@ -5012,6 +5262,17 @@ dependencies = [
|
|||
"nix",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "x25519-dalek"
|
||||
version = "1.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5a0c105152107e3b96f6a00a65e86ce82d9b125230e1c4302940eca58ff71f4f"
|
||||
dependencies = [
|
||||
"curve25519-dalek",
|
||||
"rand_core 0.5.1",
|
||||
"zeroize",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "xcursor"
|
||||
version = "0.3.4"
|
||||
|
@ -5065,3 +5326,23 @@ checksum = "56c1936c4cc7a1c9ab21a1ebb602eb942ba868cbd44a99cb7cdc5892335e1c85"
|
|||
dependencies = [
|
||||
"linked-hash-map",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zeroize"
|
||||
version = "1.6.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2a0956f1ba7c7909bfb66c2e9e4124ab6f6482560f6628b5aaeba39207c9aad9"
|
||||
dependencies = [
|
||||
"zeroize_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zeroize_derive"
|
||||
version = "1.4.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.15",
|
||||
]
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
[workspace]
|
||||
# Note: Any additions here should be repeated in default-members below.
|
||||
members = [
|
||||
"components/as-ohttp-client",
|
||||
"components/autofill",
|
||||
"components/crashtest",
|
||||
"components/fxa-client",
|
||||
|
@ -77,6 +78,7 @@ exclude = [
|
|||
# To be clear: passing the `--all` or `--workspace` arg to cargo will make it
|
||||
# use the full member set.
|
||||
default-members = [
|
||||
"components/as-ohttp-client",
|
||||
"components/autofill",
|
||||
"components/crashtest",
|
||||
"components/fxa-client",
|
||||
|
|
176
DEPENDENCIES.md
176
DEPENDENCIES.md
|
@ -40,8 +40,11 @@ the details of which are reproduced below.
|
|||
* [CC0-1.0 License: base16](#cc0-10-license-base16)
|
||||
* [ISC License: ring](#isc-license-ring)
|
||||
* [BSD-2-Clause License: arrayref](#bsd-2-clause-license-arrayref)
|
||||
* [BSD-3-Clause License: curve25519-dalek](#bsd-3-clause-license-curve25519-dalek)
|
||||
* [BSD-3-Clause License: protobuf](#bsd-3-clause-license-protobuf)
|
||||
* [BSD-3-Clause License: sqlcipher](#bsd-3-clause-license-sqlcipher)
|
||||
* [BSD-3-Clause License: subtle](#bsd-3-clause-license-subtle)
|
||||
* [BSD-3-Clause License: x25519-dalek](#bsd-3-clause-license-x25519-dalek)
|
||||
* [OpenSSL License](#openssl-license)
|
||||
* [Optional Notice: SQLite](#optional-notice-sqlite)
|
||||
* [(Apache-2.0 OR MIT) AND BSD-3-Clause License: encoding_rs](#(apache-20-or-mit)-and-bsd-3-clause-license-encoding_rs)
|
||||
|
@ -444,6 +447,9 @@ Exhibit B - "Incompatible With Secondary Licenses" Notice
|
|||
## Apache License 2.0
|
||||
|
||||
The following text applies to code linked from these dependencies:
|
||||
[aead](https://github.com/RustCrypto/traits),
|
||||
[aes-gcm](https://github.com/RustCrypto/AEADs),
|
||||
[aes](https://github.com/RustCrypto/block-ciphers),
|
||||
[ahash](https://github.com/tkaitchuck/ahash),
|
||||
[android-tzdata](https://github.com/RumovZ/android-tzdata),
|
||||
[android_system_properties](https://github.com/nical/android_system_properties),
|
||||
|
@ -454,16 +460,22 @@ The following text applies to code linked from these dependencies:
|
|||
[autocfg](https://github.com/cuviper/autocfg),
|
||||
[base64](https://github.com/marshallpierce/rust-base64),
|
||||
[basic-toml](https://github.com/dtolnay/basic-toml),
|
||||
[bhttp](https://github.com/martinthomson/ohttp),
|
||||
[bitflags](https://github.com/bitflags/bitflags),
|
||||
[block-buffer](https://github.com/RustCrypto/utils),
|
||||
[camino](https://github.com/camino-rs/camino),
|
||||
[cargo-platform](https://github.com/rust-lang/cargo),
|
||||
[cc](https://github.com/alexcrichton/cc-rs),
|
||||
[cfg-if](https://github.com/alexcrichton/cfg-if),
|
||||
[chacha20](https://github.com/RustCrypto/stream-ciphers),
|
||||
[chacha20poly1305](https://github.com/RustCrypto/AEADs),
|
||||
[chrono](https://github.com/chronotope/chrono),
|
||||
[cipher](https://github.com/RustCrypto/traits),
|
||||
[core-foundation-sys](https://github.com/servo/core-foundation-rs),
|
||||
[core-foundation](https://github.com/servo/core-foundation-rs),
|
||||
[cpufeatures](https://github.com/RustCrypto/utils),
|
||||
[crypto-mac](https://github.com/RustCrypto/traits),
|
||||
[ctr](https://github.com/RustCrypto/stream-ciphers),
|
||||
[digest](https://github.com/RustCrypto/traits),
|
||||
[dogear](https://github.com/mozilla/dogear),
|
||||
[either](https://github.com/bluss/either),
|
||||
|
@ -484,11 +496,15 @@ The following text applies to code linked from these dependencies:
|
|||
[futures-task](https://github.com/rust-lang/futures-rs),
|
||||
[futures-util](https://github.com/rust-lang/futures-rs),
|
||||
[getrandom](https://github.com/rust-random/getrandom),
|
||||
[ghash](https://github.com/RustCrypto/universal-hashes),
|
||||
[glob](https://github.com/rust-lang/glob),
|
||||
[hashbrown](https://github.com/rust-lang/hashbrown),
|
||||
[hashlink](https://github.com/kyren/hashlink),
|
||||
[heck](https://github.com/withoutboats/heck),
|
||||
[hex](https://github.com/KokaKiwi/rust-hex),
|
||||
[hkdf](https://github.com/RustCrypto/KDFs/),
|
||||
[hmac](https://github.com/RustCrypto/MACs),
|
||||
[hpke](https://github.com/rozbb/rust-hpke),
|
||||
[http](https://github.com/hyperium/http),
|
||||
[httparse](https://github.com/seanmonstar/httparse),
|
||||
[httpdate](https://github.com/pyfisch/httpdate),
|
||||
|
@ -515,6 +531,7 @@ The following text applies to code linked from these dependencies:
|
|||
[native-tls](https://github.com/sfackler/rust-native-tls),
|
||||
[num-traits](https://github.com/rust-num/num-traits),
|
||||
[num_cpus](https://github.com/seanmonstar/num_cpus),
|
||||
[ohttp](https://github.com/martinthomson/ohttp),
|
||||
[once_cell](https://github.com/matklad/once_cell),
|
||||
[opaque-debug](https://github.com/RustCrypto/utils),
|
||||
[openssl-macros](https://github.com/sfackler/rust-openssl),
|
||||
|
@ -529,6 +546,8 @@ The following text applies to code linked from these dependencies:
|
|||
[pin-utils](https://github.com/rust-lang-nursery/pin-utils),
|
||||
[pkg-config](https://github.com/rust-lang/pkg-config-rs),
|
||||
[plain](https://github.com/randomites/plain),
|
||||
[poly1305](https://github.com/RustCrypto/universal-hashes),
|
||||
[polyval](https://github.com/RustCrypto/universal-hashes),
|
||||
[ppv-lite86](https://github.com/cryptocorrosion/cryptocorrosion),
|
||||
[proc-macro2](https://github.com/dtolnay/proc-macro2),
|
||||
[prost-derive](https://github.com/tokio-rs/prost),
|
||||
|
@ -572,6 +591,7 @@ The following text applies to code linked from these dependencies:
|
|||
[unicode-ident](https://github.com/dtolnay/unicode-ident),
|
||||
[unicode-normalization](https://github.com/unicode-rs/unicode-normalization),
|
||||
[unicode-segmentation](https://github.com/unicode-rs/unicode-segmentation),
|
||||
[universal-hash](https://github.com/RustCrypto/traits),
|
||||
[url](https://github.com/servo/rust-url),
|
||||
[uuid](https://github.com/uuid-rs/uuid),
|
||||
[vcpkg](https://github.com/mcgoo/vcpkg-rs),
|
||||
|
@ -583,7 +603,9 @@ The following text applies to code linked from these dependencies:
|
|||
[windows_x86_64_gnu](https://github.com/microsoft/windows-rs),
|
||||
[windows_x86_64_msvc](https://github.com/microsoft/windows-rs),
|
||||
[xshell-macros](https://github.com/matklad/xshell),
|
||||
[xshell](https://github.com/matklad/xshell)
|
||||
[xshell](https://github.com/matklad/xshell),
|
||||
[zeroize](https://github.com/RustCrypto/utils/tree/master/zeroize),
|
||||
[zeroize_derive](https://github.com/RustCrypto/utils/tree/master/zeroize/derive)
|
||||
|
||||
```
|
||||
Apache License
|
||||
|
@ -780,7 +802,7 @@ 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
|
||||
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,
|
||||
|
@ -788,6 +810,7 @@ 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.
|
||||
|
||||
|
||||
```
|
||||
-------------
|
||||
## MIT License: SwiftKeychainWrapper
|
||||
|
@ -1930,6 +1953,80 @@ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
```
|
||||
-------------
|
||||
## BSD-3-Clause License: curve25519-dalek
|
||||
|
||||
The following text applies to code linked from these dependencies:
|
||||
[curve25519-dalek](https://github.com/dalek-cryptography/curve25519-dalek)
|
||||
|
||||
```
|
||||
Copyright (c) 2016-2021 isis agora lovecruft. All rights reserved.
|
||||
Copyright (c) 2016-2021 Henry de Valence. All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are
|
||||
met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
|
||||
2. Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
|
||||
3. Neither the name of the copyright holder nor the names of its
|
||||
contributors may be used to endorse or promote products derived from
|
||||
this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
|
||||
IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
||||
TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
|
||||
PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
|
||||
TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
========================================================================
|
||||
|
||||
Portions of curve25519-dalek were originally derived from Adam Langley's
|
||||
Go ed25519 implementation, found at <https://github.com/agl/ed25519/>,
|
||||
under the following licence:
|
||||
|
||||
========================================================================
|
||||
|
||||
Copyright (c) 2012 The Go Authors. All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are
|
||||
met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above
|
||||
copyright notice, this list of conditions and the following disclaimer
|
||||
in the documentation and/or other materials provided with the
|
||||
distribution.
|
||||
* Neither the name of Google Inc. nor the names of its
|
||||
contributors may be used to endorse or promote products derived from
|
||||
this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
|
||||
IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
||||
TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
|
||||
PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
|
||||
OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
```
|
||||
-------------
|
||||
## BSD-3-Clause License: protobuf
|
||||
|
@ -2004,6 +2101,81 @@ ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
```
|
||||
-------------
|
||||
## BSD-3-Clause License: subtle
|
||||
|
||||
The following text applies to code linked from these dependencies:
|
||||
[subtle](https://github.com/dalek-cryptography/subtle)
|
||||
|
||||
```
|
||||
Copyright (c) 2016-2017 Isis Agora Lovecruft, Henry de Valence. All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are
|
||||
met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
|
||||
2. Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
|
||||
3. Neither the name of the copyright holder nor the names of its
|
||||
contributors may be used to endorse or promote products derived from
|
||||
this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
|
||||
IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
||||
TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
|
||||
PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
|
||||
TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
```
|
||||
-------------
|
||||
## BSD-3-Clause License: x25519-dalek
|
||||
|
||||
The following text applies to code linked from these dependencies:
|
||||
[x25519-dalek](https://github.com/dalek-cryptography/x25519-dalek)
|
||||
|
||||
```
|
||||
Copyright (c) 2017-2021 isis agora lovecruft. All rights reserved.
|
||||
Copyright (c) 2019-2021 DebugSteven. All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are
|
||||
met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
|
||||
2. Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
|
||||
3. Neither the name of the copyright holder nor the names of its
|
||||
contributors may be used to endorse or promote products derived from
|
||||
this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
|
||||
IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
||||
TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
|
||||
PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
|
||||
TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
```
|
||||
-------------
|
||||
## OpenSSL License
|
||||
|
|
|
@ -0,0 +1,19 @@
|
|||
[package]
|
||||
name = "as-ohttp-client"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
authors = ["Ted Campbell <tcampbell@mozilla.com>"]
|
||||
description = "An Oblivious HTTP client for iOS applications"
|
||||
license = "MPL-2.0"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
uniffi = "0.24.1"
|
||||
thiserror = "1.0"
|
||||
bhttp = "0.3"
|
||||
ohttp = { version = "0.3", default-features = false, features = ["client", "server", "rust-hpke"]}
|
||||
parking_lot = "0.12"
|
||||
|
||||
[build-dependencies]
|
||||
uniffi = { version = "0.24.1", features=["build"]}
|
|
@ -0,0 +1,7 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
fn main() {
|
||||
uniffi::generate_scaffolding("./src/as_ohttp_client.udl").unwrap();
|
||||
}
|
|
@ -0,0 +1,110 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
import Foundation
|
||||
|
||||
public class OhttpManager {
|
||||
// The OhttpManager communicates with the relay and key server using
|
||||
// URLSession.shared.data unless an alternative networking method is
|
||||
// provided with this signature.
|
||||
public typealias NetworkFunction = (_: URLRequest) async throws -> (Data, URLResponse)
|
||||
|
||||
// Global cache to caching Gateway encryption keys. Stale entries are
|
||||
// ignored and on Gateway errors the key used should be purged and retrieved
|
||||
// again next at next network attempt.
|
||||
static var keyCache = [URL: ([UInt8], Date)]()
|
||||
|
||||
private var configUrl: URL
|
||||
private var relayUrl: URL
|
||||
private var network: NetworkFunction
|
||||
|
||||
public init(configUrl: URL,
|
||||
relayUrl: URL,
|
||||
network: @escaping NetworkFunction = URLSession.shared.data)
|
||||
{
|
||||
self.configUrl = configUrl
|
||||
self.relayUrl = relayUrl
|
||||
self.network = network
|
||||
}
|
||||
|
||||
private func fetchKey(url: URL) async throws -> [UInt8] {
|
||||
let request = URLRequest(url: url)
|
||||
if let (data, response) = try? await network(request),
|
||||
let httpResponse = response as? HTTPURLResponse,
|
||||
httpResponse.statusCode == 200
|
||||
{
|
||||
return [UInt8](data)
|
||||
}
|
||||
|
||||
throw OhttpError.KeyFetchFailed(message: "Failed to fetch encryption key")
|
||||
}
|
||||
|
||||
private func keyForGateway(gatewayConfigUrl: URL, ttl: TimeInterval) async throws -> [UInt8] {
|
||||
if let (data, timestamp) = Self.keyCache[gatewayConfigUrl] {
|
||||
if Date() < timestamp + ttl {
|
||||
// Cache Hit!
|
||||
return data
|
||||
}
|
||||
|
||||
Self.keyCache.removeValue(forKey: gatewayConfigUrl)
|
||||
}
|
||||
|
||||
let data = try await fetchKey(url: gatewayConfigUrl)
|
||||
Self.keyCache[gatewayConfigUrl] = (data, Date())
|
||||
|
||||
return data
|
||||
}
|
||||
|
||||
private func invalidateKey() {
|
||||
Self.keyCache.removeValue(forKey: configUrl)
|
||||
}
|
||||
|
||||
public func data(for request: URLRequest) async throws -> (Data, HTTPURLResponse) {
|
||||
// Get the encryption keys for Gateway
|
||||
let config = try await keyForGateway(gatewayConfigUrl: configUrl,
|
||||
ttl: TimeInterval(3600))
|
||||
|
||||
// Create an encryption session for a request-response round-trip
|
||||
let session = try OhttpSession(config: config)
|
||||
|
||||
// Encapsulate the URLRequest for the Target
|
||||
let encoded = try session.encapsulate(method: request.httpMethod ?? "GET",
|
||||
scheme: request.url!.scheme!,
|
||||
server: request.url!.host!,
|
||||
endpoint: request.url!.path,
|
||||
headers: request.allHTTPHeaderFields ?? [:],
|
||||
payload: [UInt8](request.httpBody ?? Data()))
|
||||
|
||||
// Request from Client to Relay
|
||||
var request = URLRequest(url: relayUrl)
|
||||
request.httpMethod = "POST"
|
||||
request.setValue("message/ohttp-req", forHTTPHeaderField: "Content-Type")
|
||||
request.httpBody = Data(encoded)
|
||||
|
||||
let (data, response) = try await network(request)
|
||||
|
||||
// Decapsulation failures have these codes, so invalidate any cached
|
||||
// keys in case the gateway has changed them.
|
||||
if let httpResponse = response as? HTTPURLResponse,
|
||||
httpResponse.statusCode == 400 ||
|
||||
httpResponse.statusCode == 401
|
||||
{
|
||||
invalidateKey()
|
||||
}
|
||||
|
||||
guard let httpResponse = response as? HTTPURLResponse,
|
||||
httpResponse.statusCode == 200
|
||||
else {
|
||||
throw OhttpError.RelayFailed(message: "Network errors communicating with Relay / Gateway")
|
||||
}
|
||||
|
||||
// Decapsulate the Target response into a HTTPURLResponse
|
||||
let message = try session.decapsulate(encoded: [UInt8](data))
|
||||
return (Data(message.payload),
|
||||
HTTPURLResponse(url: request.url!,
|
||||
statusCode: Int(message.statusCode),
|
||||
httpVersion: "HTTP/1.1",
|
||||
headerFields: message.headers)!)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,70 @@
|
|||
namespace as_ohttp_client {
|
||||
};
|
||||
|
||||
[Error]
|
||||
enum OhttpError {
|
||||
"KeyFetchFailed",
|
||||
"MalformedKeyConfig",
|
||||
"UnsupportedKeyConfig",
|
||||
"InvalidSession",
|
||||
"RelayFailed",
|
||||
"CannotEncodeMessage",
|
||||
"MalformedMessage",
|
||||
"DuplicateHeaders",
|
||||
};
|
||||
|
||||
// The decrypted response from the Gateway/Target
|
||||
dictionary OhttpResponse {
|
||||
u16 status_code;
|
||||
record<string, string> headers;
|
||||
sequence<u8> payload;
|
||||
};
|
||||
|
||||
// Each OHTTP request-reply exchange needs to create an OhttpSession
|
||||
// object to manage encryption state.
|
||||
interface OhttpSession {
|
||||
// Initialize encryption state based on specific Gateway key config
|
||||
[Throws=OhttpError]
|
||||
constructor([ByRef] sequence<u8> config);
|
||||
|
||||
// Encapsulate an HTTP request as Binary HTTP and then encrypt that
|
||||
// payload using HPKE. The caller is reponsible for sending the
|
||||
// resulting message to the Relay.
|
||||
[Throws=OhttpError]
|
||||
sequence<u8> encapsulate([ByRef] string method,
|
||||
[ByRef] string scheme,
|
||||
[ByRef] string server,
|
||||
[ByRef] string endpoint,
|
||||
record<string, string> headers,
|
||||
[ByRef] sequence<u8> payload);
|
||||
|
||||
// Decypt and unpack the response from the Relay for the previously
|
||||
// encapsulated request. You must use the same OhttpSession that
|
||||
// generated the request message.
|
||||
[Throws=OhttpError]
|
||||
OhttpResponse decapsulate([ByRef] sequence<u8> encoded);
|
||||
};
|
||||
|
||||
dictionary TestServerRequest {
|
||||
string method;
|
||||
string scheme;
|
||||
string server;
|
||||
string endpoint;
|
||||
record<string, string> headers;
|
||||
sequence<u8> payload;
|
||||
};
|
||||
|
||||
// A testing interface for decrypting and responding to OHTTP messages. This
|
||||
// should only be used for testing.
|
||||
interface OhttpTestServer {
|
||||
constructor();
|
||||
|
||||
// Return the unique encryption key config for this instance of test server.
|
||||
sequence<u8> get_config();
|
||||
|
||||
[Throws=OhttpError]
|
||||
TestServerRequest receive([ByRef] sequence<u8> message);
|
||||
|
||||
[Throws=OhttpError]
|
||||
sequence<u8> respond(OhttpResponse response);
|
||||
};
|
|
@ -0,0 +1,298 @@
|
|||
extern crate bhttp;
|
||||
extern crate ohttp;
|
||||
|
||||
use parking_lot::Mutex;
|
||||
use std::collections::HashMap;
|
||||
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
pub enum OhttpError {
|
||||
#[error("Failed to fetch encryption key")]
|
||||
KeyFetchFailed,
|
||||
|
||||
#[error("OHTTP key config is malformed")]
|
||||
MalformedKeyConfig,
|
||||
|
||||
#[error("Unsupported OHTTP encryption algorithm")]
|
||||
UnsupportedKeyConfig,
|
||||
|
||||
#[error("OhttpSession is in invalid state")]
|
||||
InvalidSession,
|
||||
|
||||
#[error("Network errors communicating with Relay / Gateway")]
|
||||
RelayFailed,
|
||||
|
||||
#[error("Cannot encode message as BHTTP/OHTTP")]
|
||||
CannotEncodeMessage,
|
||||
|
||||
#[error("Cannot decode OHTTP/BHTTP message")]
|
||||
MalformedMessage,
|
||||
|
||||
#[error("Duplicate HTTP response headers")]
|
||||
DuplicateHeaders,
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
enum ExchangeState {
|
||||
#[default]
|
||||
Invalid,
|
||||
Request(ohttp::ClientRequest),
|
||||
Response(ohttp::ClientResponse),
|
||||
}
|
||||
|
||||
pub struct OhttpSession {
|
||||
state: Mutex<ExchangeState>,
|
||||
}
|
||||
|
||||
pub struct OhttpResponse {
|
||||
status_code: u16,
|
||||
headers: HashMap<String, String>,
|
||||
payload: Vec<u8>,
|
||||
}
|
||||
|
||||
/// Transform the headers from a BHTTP message into a HashMap for use from Swift
|
||||
/// later. If there are duplicate errors, we currently raise an error.
|
||||
fn headers_to_map(message: &bhttp::Message) -> Result<HashMap<String, String>, OhttpError> {
|
||||
let mut headers = HashMap::new();
|
||||
|
||||
for field in message.header().iter() {
|
||||
if headers
|
||||
.insert(
|
||||
std::str::from_utf8(field.name())
|
||||
.map_err(|_| OhttpError::MalformedMessage)?
|
||||
.into(),
|
||||
std::str::from_utf8(field.value())
|
||||
.map_err(|_| OhttpError::MalformedMessage)?
|
||||
.into(),
|
||||
)
|
||||
.is_some()
|
||||
{
|
||||
return Err(OhttpError::DuplicateHeaders);
|
||||
}
|
||||
}
|
||||
|
||||
Ok(headers)
|
||||
}
|
||||
|
||||
impl OhttpSession {
|
||||
/// Create a new encryption session for use with specific key configuration
|
||||
pub fn new(config: &[u8]) -> Result<Self, OhttpError> {
|
||||
let request = ohttp::ClientRequest::new(config).map_err(|e| match e {
|
||||
ohttp::Error::Unsupported => OhttpError::UnsupportedKeyConfig,
|
||||
_ => OhttpError::MalformedKeyConfig,
|
||||
})?;
|
||||
|
||||
let state = Mutex::new(ExchangeState::Request(request));
|
||||
Ok(OhttpSession { state })
|
||||
}
|
||||
|
||||
/// Encode an HTTP request in Binary HTTP format and then encrypt it into an
|
||||
/// Oblivious HTTP request message.
|
||||
pub fn encapsulate(
|
||||
&self,
|
||||
method: &str,
|
||||
scheme: &str,
|
||||
server: &str,
|
||||
endpoint: &str,
|
||||
mut headers: HashMap<String, String>,
|
||||
payload: &[u8],
|
||||
) -> Result<Vec<u8>, OhttpError> {
|
||||
let mut message =
|
||||
bhttp::Message::request(method.into(), scheme.into(), server.into(), endpoint.into());
|
||||
|
||||
for (k, v) in headers.drain() {
|
||||
message.put_header(k, v);
|
||||
}
|
||||
|
||||
message.write_content(payload);
|
||||
|
||||
let mut encoded = vec![];
|
||||
message
|
||||
.write_bhttp(bhttp::Mode::KnownLength, &mut encoded)
|
||||
.map_err(|_| OhttpError::CannotEncodeMessage)?;
|
||||
|
||||
let mut state = self.state.lock();
|
||||
let request = match std::mem::take(&mut *state) {
|
||||
ExchangeState::Request(request) => request,
|
||||
_ => return Err(OhttpError::InvalidSession),
|
||||
};
|
||||
let (capsule, response) = request
|
||||
.encapsulate(&encoded)
|
||||
.map_err(|_| OhttpError::CannotEncodeMessage)?;
|
||||
*state = ExchangeState::Response(response);
|
||||
|
||||
Ok(capsule)
|
||||
}
|
||||
|
||||
/// Decode an OHTTP response returned in response to a request encoded on
|
||||
/// this session.
|
||||
pub fn decapsulate(&self, encoded: &[u8]) -> Result<OhttpResponse, OhttpError> {
|
||||
let mut state = self.state.lock();
|
||||
let decoder = match std::mem::take(&mut *state) {
|
||||
ExchangeState::Response(response) => response,
|
||||
_ => return Err(OhttpError::InvalidSession),
|
||||
};
|
||||
let binary = decoder
|
||||
.decapsulate(encoded)
|
||||
.map_err(|_| OhttpError::MalformedMessage)?;
|
||||
|
||||
let mut cursor = std::io::Cursor::new(binary);
|
||||
let message =
|
||||
bhttp::Message::read_bhttp(&mut cursor).map_err(|_| OhttpError::MalformedMessage)?;
|
||||
|
||||
let headers = headers_to_map(&message)?;
|
||||
|
||||
Ok(OhttpResponse {
|
||||
status_code: match message.control() {
|
||||
bhttp::ControlData::Response(sc) => *sc,
|
||||
_ => return Err(OhttpError::InvalidSession),
|
||||
},
|
||||
headers,
|
||||
payload: message.content().into(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
pub struct OhttpTestServer {
|
||||
server: Mutex<ohttp::Server>,
|
||||
state: Mutex<Option<ohttp::ServerResponse>>,
|
||||
config: Vec<u8>,
|
||||
}
|
||||
|
||||
pub struct TestServerRequest {
|
||||
method: String,
|
||||
scheme: String,
|
||||
server: String,
|
||||
endpoint: String,
|
||||
headers: HashMap<String, String>,
|
||||
payload: Vec<u8>,
|
||||
}
|
||||
|
||||
impl OhttpTestServer {
|
||||
/// Create a simple OHTTP server to decrypt and respond to OHTTP messages in
|
||||
/// testing. The key is randomly generated.
|
||||
fn new() -> Self {
|
||||
let key = ohttp::KeyConfig::new(
|
||||
0x01,
|
||||
ohttp::hpke::Kem::X25519Sha256,
|
||||
vec![ohttp::SymmetricSuite::new(
|
||||
ohttp::hpke::Kdf::HkdfSha256,
|
||||
ohttp::hpke::Aead::Aes128Gcm,
|
||||
)],
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let config = key.encode().unwrap();
|
||||
let server = ohttp::Server::new(key).unwrap();
|
||||
|
||||
OhttpTestServer {
|
||||
server: Mutex::new(server),
|
||||
state: Mutex::new(Option::None),
|
||||
config,
|
||||
}
|
||||
}
|
||||
|
||||
/// Return a copy of the key config for clients to use.
|
||||
fn get_config(&self) -> Vec<u8> {
|
||||
self.config.clone()
|
||||
}
|
||||
|
||||
/// Decode an OHTTP request message and return the cleartext contents. This
|
||||
/// also updates the internal server state so that a response message can be
|
||||
/// generated.
|
||||
fn receive(&self, message: &[u8]) -> Result<TestServerRequest, OhttpError> {
|
||||
let (encoded, response) = self
|
||||
.server
|
||||
.lock()
|
||||
.decapsulate(message)
|
||||
.map_err(|_| OhttpError::MalformedMessage)?;
|
||||
let mut cursor = std::io::Cursor::new(encoded);
|
||||
let message =
|
||||
bhttp::Message::read_bhttp(&mut cursor).map_err(|_| OhttpError::MalformedMessage)?;
|
||||
|
||||
*self.state.lock() = Some(response);
|
||||
|
||||
let headers = headers_to_map(&message)?;
|
||||
|
||||
match message.control() {
|
||||
bhttp::ControlData::Request {
|
||||
method,
|
||||
scheme,
|
||||
authority,
|
||||
path,
|
||||
} => Ok(TestServerRequest {
|
||||
method: String::from_utf8_lossy(method).into(),
|
||||
scheme: String::from_utf8_lossy(scheme).into(),
|
||||
server: String::from_utf8_lossy(authority).into(),
|
||||
endpoint: String::from_utf8_lossy(path).into(),
|
||||
headers,
|
||||
payload: message.content().into(),
|
||||
}),
|
||||
_ => Err(OhttpError::MalformedMessage),
|
||||
}
|
||||
}
|
||||
|
||||
/// Encode an OHTTP response keyed to the last message received.
|
||||
fn respond(&self, response: OhttpResponse) -> Result<Vec<u8>, OhttpError> {
|
||||
let state = self.state.lock().take().unwrap();
|
||||
|
||||
let mut message = bhttp::Message::response(response.status_code);
|
||||
message.write_content(&response.payload);
|
||||
|
||||
for (k, v) in response.headers {
|
||||
message.put_header(k, v);
|
||||
}
|
||||
|
||||
let mut encoded = vec![];
|
||||
message
|
||||
.write_bhttp(bhttp::Mode::KnownLength, &mut encoded)
|
||||
.map_err(|_| OhttpError::CannotEncodeMessage)?;
|
||||
|
||||
state
|
||||
.encapsulate(&encoded)
|
||||
.map_err(|_| OhttpError::CannotEncodeMessage)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_smoke() {
|
||||
let server = OhttpTestServer::new();
|
||||
let config = server.get_config();
|
||||
|
||||
let body: Vec<u8> = vec![0x00, 0x01, 0x02];
|
||||
let header = HashMap::from([
|
||||
("Content-Type".into(), "application/octet-stream".into()),
|
||||
("X-Header".into(), "value".into()),
|
||||
]);
|
||||
|
||||
let session = OhttpSession::new(&config).unwrap();
|
||||
let mut message = session
|
||||
.encapsulate("GET", "https", "example.com", "/api", header.clone(), &body)
|
||||
.unwrap();
|
||||
|
||||
let request = server.receive(&message).unwrap();
|
||||
assert_eq!(request.method, "GET");
|
||||
assert_eq!(request.scheme, "https");
|
||||
assert_eq!(request.server, "example.com");
|
||||
assert_eq!(request.endpoint, "/api");
|
||||
assert_eq!(request.headers, header);
|
||||
|
||||
message = server
|
||||
.respond(OhttpResponse {
|
||||
status_code: 200,
|
||||
headers: header.clone(),
|
||||
payload: body.clone(),
|
||||
})
|
||||
.unwrap();
|
||||
|
||||
let response = session.decapsulate(&message).unwrap();
|
||||
assert_eq!(response.status_code, 200);
|
||||
assert_eq!(response.headers, header);
|
||||
assert_eq!(response.payload, body);
|
||||
}
|
||||
}
|
||||
|
||||
uniffi::include_scaffolding!("as_ohttp_client");
|
|
@ -0,0 +1,3 @@
|
|||
[bindings.swift]
|
||||
ffi_module_name = "MozillaRustComponents"
|
||||
ffi_module_filename = "as_ohttp_clientFFI"
|
|
@ -25,3 +25,4 @@ remote_settings = { path = "../../components/remote_settings" }
|
|||
sync15 = {path = "../../components/sync15"}
|
||||
error-support = { path = "../../components/support/error" }
|
||||
sync_manager = { path = "../../components/sync_manager" }
|
||||
as-ohttp-client = { path = "../../components/as-ohttp-client" }
|
||||
|
|
|
@ -37,7 +37,10 @@ the details of which are reproduced below.
|
|||
* [CC0-1.0 License: base16](#cc0-10-license-base16)
|
||||
* [ISC License: ring](#isc-license-ring)
|
||||
* [BSD-2-Clause License: arrayref](#bsd-2-clause-license-arrayref)
|
||||
* [BSD-3-Clause License: curve25519-dalek](#bsd-3-clause-license-curve25519-dalek)
|
||||
* [BSD-3-Clause License: sqlcipher](#bsd-3-clause-license-sqlcipher)
|
||||
* [BSD-3-Clause License: subtle](#bsd-3-clause-license-subtle)
|
||||
* [BSD-3-Clause License: x25519-dalek](#bsd-3-clause-license-x25519-dalek)
|
||||
* [Optional Notice: SQLite](#optional-notice-sqlite)
|
||||
* [(Apache-2.0 OR MIT) AND BSD-3-Clause License: encoding_rs](#(apache-20-or-mit)-and-bsd-3-clause-license-encoding_rs)
|
||||
-------------
|
||||
|
@ -439,6 +442,9 @@ Exhibit B - "Incompatible With Secondary Licenses" Notice
|
|||
## Apache License 2.0
|
||||
|
||||
The following text applies to code linked from these dependencies:
|
||||
[aead](https://github.com/RustCrypto/traits),
|
||||
[aes-gcm](https://github.com/RustCrypto/AEADs),
|
||||
[aes](https://github.com/RustCrypto/block-ciphers),
|
||||
[ahash](https://github.com/tkaitchuck/ahash),
|
||||
[anyhow](https://github.com/dtolnay/anyhow),
|
||||
[askama](https://github.com/djc/askama),
|
||||
|
@ -447,16 +453,22 @@ The following text applies to code linked from these dependencies:
|
|||
[autocfg](https://github.com/cuviper/autocfg),
|
||||
[base64](https://github.com/marshallpierce/rust-base64),
|
||||
[basic-toml](https://github.com/dtolnay/basic-toml),
|
||||
[bhttp](https://github.com/martinthomson/ohttp),
|
||||
[bitflags](https://github.com/bitflags/bitflags),
|
||||
[block-buffer](https://github.com/RustCrypto/utils),
|
||||
[camino](https://github.com/camino-rs/camino),
|
||||
[cargo-platform](https://github.com/rust-lang/cargo),
|
||||
[cc](https://github.com/alexcrichton/cc-rs),
|
||||
[cfg-if](https://github.com/alexcrichton/cfg-if),
|
||||
[chacha20](https://github.com/RustCrypto/stream-ciphers),
|
||||
[chacha20poly1305](https://github.com/RustCrypto/AEADs),
|
||||
[chrono](https://github.com/chronotope/chrono),
|
||||
[cipher](https://github.com/RustCrypto/traits),
|
||||
[core-foundation-sys](https://github.com/servo/core-foundation-rs),
|
||||
[core-foundation](https://github.com/servo/core-foundation-rs),
|
||||
[cpufeatures](https://github.com/RustCrypto/utils),
|
||||
[crypto-mac](https://github.com/RustCrypto/traits),
|
||||
[ctr](https://github.com/RustCrypto/stream-ciphers),
|
||||
[digest](https://github.com/RustCrypto/traits),
|
||||
[dogear](https://github.com/mozilla/dogear),
|
||||
[either](https://github.com/bluss/either),
|
||||
|
@ -475,11 +487,15 @@ The following text applies to code linked from these dependencies:
|
|||
[futures-task](https://github.com/rust-lang/futures-rs),
|
||||
[futures-util](https://github.com/rust-lang/futures-rs),
|
||||
[getrandom](https://github.com/rust-random/getrandom),
|
||||
[ghash](https://github.com/RustCrypto/universal-hashes),
|
||||
[glob](https://github.com/rust-lang/glob),
|
||||
[hashbrown](https://github.com/rust-lang/hashbrown),
|
||||
[hashlink](https://github.com/kyren/hashlink),
|
||||
[heck](https://github.com/withoutboats/heck),
|
||||
[hex](https://github.com/KokaKiwi/rust-hex),
|
||||
[hkdf](https://github.com/RustCrypto/KDFs/),
|
||||
[hmac](https://github.com/RustCrypto/MACs),
|
||||
[hpke](https://github.com/rozbb/rust-hpke),
|
||||
[http](https://github.com/hyperium/http),
|
||||
[httparse](https://github.com/seanmonstar/httparse),
|
||||
[httpdate](https://github.com/pyfisch/httpdate),
|
||||
|
@ -504,6 +520,7 @@ The following text applies to code linked from these dependencies:
|
|||
[native-tls](https://github.com/sfackler/rust-native-tls),
|
||||
[num-traits](https://github.com/rust-num/num-traits),
|
||||
[num_cpus](https://github.com/seanmonstar/num_cpus),
|
||||
[ohttp](https://github.com/martinthomson/ohttp),
|
||||
[once_cell](https://github.com/matklad/once_cell),
|
||||
[opaque-debug](https://github.com/RustCrypto/utils),
|
||||
[parking_lot](https://github.com/Amanieu/parking_lot),
|
||||
|
@ -514,6 +531,8 @@ The following text applies to code linked from these dependencies:
|
|||
[pin-utils](https://github.com/rust-lang-nursery/pin-utils),
|
||||
[pkg-config](https://github.com/rust-lang/pkg-config-rs),
|
||||
[plain](https://github.com/randomites/plain),
|
||||
[poly1305](https://github.com/RustCrypto/universal-hashes),
|
||||
[polyval](https://github.com/RustCrypto/universal-hashes),
|
||||
[ppv-lite86](https://github.com/cryptocorrosion/cryptocorrosion),
|
||||
[proc-macro2](https://github.com/dtolnay/proc-macro2),
|
||||
[prost-derive](https://github.com/tokio-rs/prost),
|
||||
|
@ -557,12 +576,15 @@ The following text applies to code linked from these dependencies:
|
|||
[unicode-ident](https://github.com/dtolnay/unicode-ident),
|
||||
[unicode-normalization](https://github.com/unicode-rs/unicode-normalization),
|
||||
[unicode-segmentation](https://github.com/unicode-rs/unicode-segmentation),
|
||||
[universal-hash](https://github.com/RustCrypto/traits),
|
||||
[url](https://github.com/servo/rust-url),
|
||||
[uuid](https://github.com/uuid-rs/uuid),
|
||||
[vcpkg](https://github.com/mcgoo/vcpkg-rs),
|
||||
[version_check](https://github.com/SergioBenitez/version_check),
|
||||
[xshell-macros](https://github.com/matklad/xshell),
|
||||
[xshell](https://github.com/matklad/xshell)
|
||||
[xshell](https://github.com/matklad/xshell),
|
||||
[zeroize](https://github.com/RustCrypto/utils/tree/master/zeroize),
|
||||
[zeroize_derive](https://github.com/RustCrypto/utils/tree/master/zeroize/derive)
|
||||
|
||||
```
|
||||
Apache License
|
||||
|
@ -759,7 +781,7 @@ 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
|
||||
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,
|
||||
|
@ -767,6 +789,7 @@ 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.
|
||||
|
||||
|
||||
```
|
||||
-------------
|
||||
## MIT License: SwiftKeychainWrapper
|
||||
|
@ -1831,6 +1854,80 @@ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
```
|
||||
-------------
|
||||
## BSD-3-Clause License: curve25519-dalek
|
||||
|
||||
The following text applies to code linked from these dependencies:
|
||||
[curve25519-dalek](https://github.com/dalek-cryptography/curve25519-dalek)
|
||||
|
||||
```
|
||||
Copyright (c) 2016-2021 isis agora lovecruft. All rights reserved.
|
||||
Copyright (c) 2016-2021 Henry de Valence. All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are
|
||||
met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
|
||||
2. Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
|
||||
3. Neither the name of the copyright holder nor the names of its
|
||||
contributors may be used to endorse or promote products derived from
|
||||
this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
|
||||
IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
||||
TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
|
||||
PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
|
||||
TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
========================================================================
|
||||
|
||||
Portions of curve25519-dalek were originally derived from Adam Langley's
|
||||
Go ed25519 implementation, found at <https://github.com/agl/ed25519/>,
|
||||
under the following licence:
|
||||
|
||||
========================================================================
|
||||
|
||||
Copyright (c) 2012 The Go Authors. All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are
|
||||
met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above
|
||||
copyright notice, this list of conditions and the following disclaimer
|
||||
in the documentation and/or other materials provided with the
|
||||
distribution.
|
||||
* Neither the name of Google Inc. nor the names of its
|
||||
contributors may be used to endorse or promote products derived from
|
||||
this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
|
||||
IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
||||
TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
|
||||
PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
|
||||
OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
```
|
||||
-------------
|
||||
## BSD-3-Clause License: sqlcipher
|
||||
|
@ -1864,6 +1961,81 @@ ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
```
|
||||
-------------
|
||||
## BSD-3-Clause License: subtle
|
||||
|
||||
The following text applies to code linked from these dependencies:
|
||||
[subtle](https://github.com/dalek-cryptography/subtle)
|
||||
|
||||
```
|
||||
Copyright (c) 2016-2017 Isis Agora Lovecruft, Henry de Valence. All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are
|
||||
met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
|
||||
2. Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
|
||||
3. Neither the name of the copyright holder nor the names of its
|
||||
contributors may be used to endorse or promote products derived from
|
||||
this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
|
||||
IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
||||
TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
|
||||
PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
|
||||
TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
```
|
||||
-------------
|
||||
## BSD-3-Clause License: x25519-dalek
|
||||
|
||||
The following text applies to code linked from these dependencies:
|
||||
[x25519-dalek](https://github.com/dalek-cryptography/x25519-dalek)
|
||||
|
||||
```
|
||||
Copyright (c) 2017-2021 isis agora lovecruft. All rights reserved.
|
||||
Copyright (c) 2019-2021 DebugSteven. All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are
|
||||
met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
|
||||
2. Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
|
||||
3. Neither the name of the copyright holder nor the names of its
|
||||
contributors may be used to endorse or promote products derived from
|
||||
this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
|
||||
IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
||||
TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
|
||||
PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
|
||||
TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
```
|
||||
-------------
|
||||
## Optional Notice: SQLite
|
||||
|
|
|
@ -19,3 +19,4 @@
|
|||
#import "errorFFI.h"
|
||||
#import "syncmanagerFFI.h"
|
||||
#import "remote_settingsFFI.h"
|
||||
#import "as_ohttp_clientFFI.h"
|
||||
|
|
|
@ -71,6 +71,9 @@
|
|||
45CC574A28AD9C86006D55AA /* errorsupport.udl in Sources */ = {isa = PBXBuildFile; fileRef = 45CC574828AD9C31006D55AA /* errorsupport.udl */; };
|
||||
AB9C392E2A0DA61900AF5ADE /* remote_settings.udl in Sources */ = {isa = PBXBuildFile; fileRef = AB65933B2A0C524E00DBF059 /* remote_settings.udl */; };
|
||||
F40A9DCD29765EB20033D10E /* sync15.udl in Sources */ = {isa = PBXBuildFile; fileRef = F40A9DCB29765DCB0033D10E /* sync15.udl */; };
|
||||
F54D38102A5862E4005087FB /* OhttpTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = F54D380F2A5862E4005087FB /* OhttpTests.swift */; };
|
||||
F54D38112A5E09F4005087FB /* as_ohttp_client.udl in Sources */ = {isa = PBXBuildFile; fileRef = F54D380C2A5732A3005087FB /* as_ohttp_client.udl */; };
|
||||
F596D2E02A68922100C8A817 /* OhttpManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = F596D2DF2A68922100C8A817 /* OhttpManager.swift */; };
|
||||
F814DE8029DF762800FD26F5 /* SyncManagerTelemetryTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = F814DE7F29DF762800FD26F5 /* SyncManagerTelemetryTests.swift */; };
|
||||
F81C7B9829DE305C00FAF8F9 /* SyncManagerTelemetry.swift in Sources */ = {isa = PBXBuildFile; fileRef = F81C7B9729DE305C00FAF8F9 /* SyncManagerTelemetry.swift */; };
|
||||
F81C7B9A29DE309C00FAF8F9 /* SyncManagerComponent.swift in Sources */ = {isa = PBXBuildFile; fileRef = F81C7B9929DE309C00FAF8F9 /* SyncManagerComponent.swift */; };
|
||||
|
@ -206,6 +209,11 @@
|
|||
F40A9DCB29765DCB0033D10E /* sync15.udl */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; name = sync15.udl; path = ../../../components/sync15/src/sync15.udl; sourceTree = SOURCE_ROOT; };
|
||||
F4FCED2E2976605400BA127E /* sync15FFI.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = sync15FFI.h; path = ../../../components/sync15/ios/Generated/sync15FFI.h; sourceTree = SOURCE_ROOT; };
|
||||
F4FCED2F2976605400BA127E /* sync15.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = sync15.swift; path = ../../../components/sync15/ios/Generated/sync15.swift; sourceTree = SOURCE_ROOT; };
|
||||
F54D38072A564EC9005087FB /* as_ohttp_client.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = as_ohttp_client.swift; path = ../../ios/Generated/as_ohttp_client.swift; sourceTree = "<group>"; };
|
||||
F54D38082A564EC9005087FB /* as_ohttp_clientFFI.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = as_ohttp_clientFFI.h; path = ../../ios/Generated/as_ohttp_clientFFI.h; sourceTree = "<group>"; };
|
||||
F54D380C2A5732A3005087FB /* as_ohttp_client.udl */ = {isa = PBXFileReference; lastKnownFileType = text; name = as_ohttp_client.udl; path = ../src/as_ohttp_client.udl; sourceTree = "<group>"; };
|
||||
F54D380F2A5862E4005087FB /* OhttpTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OhttpTests.swift; sourceTree = "<group>"; };
|
||||
F596D2DF2A68922100C8A817 /* OhttpManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = OhttpManager.swift; path = ASOhttpClient/OhttpManager.swift; sourceTree = "<group>"; };
|
||||
F814DE7F29DF762800FD26F5 /* SyncManagerTelemetryTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SyncManagerTelemetryTests.swift; sourceTree = "<group>"; };
|
||||
F81C7B9729DE305C00FAF8F9 /* SyncManagerTelemetry.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = SyncManagerTelemetry.swift; path = ../../../components/sync_manager/ios/SyncManager/SyncManagerTelemetry.swift; sourceTree = SOURCE_ROOT; };
|
||||
F81C7B9929DE309C00FAF8F9 /* SyncManagerComponent.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = SyncManagerComponent.swift; path = ../../../components/sync_manager/ios/SyncManager/SyncManagerComponent.swift; sourceTree = SOURCE_ROOT; };
|
||||
|
@ -337,6 +345,7 @@
|
|||
1BBAC4FA27AE049500DAFEF2 /* MozillaTestServices */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
F54D38032A564653005087FB /* ASOhttpClient */,
|
||||
AB65933A2A0C51F300DBF059 /* RemoteSettings */,
|
||||
F8AAC1CB298B40B8000BCDEC /* SyncManager */,
|
||||
15DAE2082944142000DB06FE /* Autofill */,
|
||||
|
@ -372,6 +381,7 @@
|
|||
F85ED648299C1F49005EEF36 /* RustSyncTelemetryPingTests.swift */,
|
||||
F814DE7F29DF762800FD26F5 /* SyncManagerTelemetryTests.swift */,
|
||||
39EE00FE29F6DBBA001E7758 /* NimbusArgumentProcessorTests.swift */,
|
||||
F54D380F2A5862E4005087FB /* OhttpTests.swift */,
|
||||
);
|
||||
path = MozillaTestServicesTests;
|
||||
sourceTree = SOURCE_ROOT;
|
||||
|
@ -586,6 +596,26 @@
|
|||
path = Generated;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
F54D38032A564653005087FB /* ASOhttpClient */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
F54D38042A564653005087FB /* Generated */,
|
||||
F54D380C2A5732A3005087FB /* as_ohttp_client.udl */,
|
||||
F596D2DF2A68922100C8A817 /* OhttpManager.swift */,
|
||||
);
|
||||
name = ASOhttpClient;
|
||||
path = "../../../components/as-ohttp-client/ios";
|
||||
sourceTree = SOURCE_ROOT;
|
||||
};
|
||||
F54D38042A564653005087FB /* Generated */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
F54D38072A564EC9005087FB /* as_ohttp_client.swift */,
|
||||
F54D38082A564EC9005087FB /* as_ohttp_clientFFI.h */,
|
||||
);
|
||||
path = Generated;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
F8AAC1CB298B40B8000BCDEC /* SyncManager */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
|
@ -742,6 +772,7 @@
|
|||
isa = PBXSourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
F54D38112A5E09F4005087FB /* as_ohttp_client.udl in Sources */,
|
||||
AB9C392E2A0DA61900AF5ADE /* remote_settings.udl in Sources */,
|
||||
39AD326F2988468B00E42E13 /* FeatureManifestInterface.swift in Sources */,
|
||||
F8C88393298B4BF80006E9E9 /* syncmanager.udl in Sources */,
|
||||
|
@ -771,6 +802,7 @@
|
|||
1B3BC94627B1D6A500229CF6 /* ResultError.swift in Sources */,
|
||||
1B3BC98327B1D9B700229CF6 /* FeatureHolder.swift in Sources */,
|
||||
1B3BC98527B1D9B800229CF6 /* NimbusMessagingHelpers.swift in Sources */,
|
||||
F596D2E02A68922100C8A817 /* OhttpManager.swift in Sources */,
|
||||
395EFD6E2966EB6D00D24B97 /* Bundle+.swift in Sources */,
|
||||
1BF50F1427B1E17B00A9C8A5 /* PersistedFirefoxAccount.swift in Sources */,
|
||||
1BF50F1027B1E17B00A9C8A5 /* FxAccountStorage.swift in Sources */,
|
||||
|
@ -817,6 +849,7 @@
|
|||
1B3BC94F27B1D92800229CF6 /* NimbusTests.swift in Sources */,
|
||||
1BF50F1927B1E19500A9C8A5 /* FxAccountManagerTests.swift in Sources */,
|
||||
39083AAB29561E2400FDD302 /* OperationTests.swift in Sources */,
|
||||
F54D38102A5862E4005087FB /* OhttpTests.swift in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
|
|
|
@ -0,0 +1,316 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
import XCTest
|
||||
|
||||
@testable import MozillaTestServices
|
||||
|
||||
// These tests cover the integration of the underlying Rust libraries into Swift
|
||||
// URL{Request,Response} data types, as well as the key management and error
|
||||
// handling logic of OhttpManager class.
|
||||
|
||||
// A testing model of Client, KeyConfigEndpoint, Relay, Gateway, and Target. This
|
||||
// includes an OHTTP decryption server to decode messages, but does not model TLS,
|
||||
// etc.
|
||||
class FakeOhttpNetwork {
|
||||
let server = OhttpTestServer()
|
||||
let configURL = URL(string: "https://gateway.example.com/ohttp-configs")!
|
||||
let relayURL = URL(string: "https://relay.example.com/")!
|
||||
|
||||
// Create an instance of OhttpManager with networking hooks installed to
|
||||
// send requests to this model instead of the Internet.
|
||||
func newOhttpManager() -> OhttpManager {
|
||||
OhttpManager(configUrl: configURL,
|
||||
relayUrl: relayURL,
|
||||
network: client)
|
||||
}
|
||||
|
||||
// Response helpers
|
||||
func statusResponse(request: URLRequest, statusCode: Int) -> (Data, HTTPURLResponse) {
|
||||
(Data(),
|
||||
HTTPURLResponse(url: request.url!,
|
||||
statusCode: statusCode,
|
||||
httpVersion: "HTTP/1.1",
|
||||
headerFields: [:])!)
|
||||
}
|
||||
|
||||
func dataResponse(request: URLRequest, body: Data, contentType: String) -> (Data, HTTPURLResponse) {
|
||||
(body,
|
||||
HTTPURLResponse(url: request.url!,
|
||||
statusCode: 200,
|
||||
httpVersion: "HTTP/1.1",
|
||||
headerFields: ["Content-Length": String(body.count),
|
||||
"Content-Type": contentType])!)
|
||||
}
|
||||
|
||||
//
|
||||
// Network node models
|
||||
//
|
||||
func client(_ request: URLRequest) async throws -> (Data, URLResponse) {
|
||||
switch request.url {
|
||||
case configURL: return config(request)
|
||||
case relayURL: return relay(request)
|
||||
default: throw NSError()
|
||||
}
|
||||
}
|
||||
|
||||
func config(_ request: URLRequest) -> (Data, URLResponse) {
|
||||
let key = server.getConfig()
|
||||
return dataResponse(request: request,
|
||||
body: Data(key),
|
||||
contentType: "application/octet-stream")
|
||||
}
|
||||
|
||||
func relay(_ request: URLRequest) -> (Data, URLResponse) {
|
||||
return gateway(request)
|
||||
}
|
||||
|
||||
func gateway(_ request: URLRequest) -> (Data, URLResponse) {
|
||||
let inner = try! server.receive(message: [UInt8](request.httpBody!))
|
||||
|
||||
// Unwrap OHTTP/BHTTP
|
||||
var innerUrl = URLComponents()
|
||||
innerUrl.scheme = inner.scheme
|
||||
innerUrl.host = inner.server
|
||||
innerUrl.path = inner.endpoint
|
||||
var innerRequest = URLRequest(url: innerUrl.url!)
|
||||
innerRequest.httpMethod = inner.method
|
||||
innerRequest.httpBody = Data(inner.payload)
|
||||
for (k, v) in inner.headers {
|
||||
innerRequest.setValue(v, forHTTPHeaderField: k)
|
||||
}
|
||||
|
||||
let (innerData, innerResponse) = target(innerRequest)
|
||||
|
||||
// Wrap with BHTTP/OHTTP
|
||||
var headers: [String: String] = [:]
|
||||
for (k, v) in innerResponse.allHeaderFields {
|
||||
headers[k as! String] = v as? String
|
||||
}
|
||||
let reply = try! server.respond(response: OhttpResponse(statusCode: UInt16(innerResponse.statusCode),
|
||||
headers: headers,
|
||||
payload: [UInt8](innerData)))
|
||||
return dataResponse(request: request,
|
||||
body: Data(reply),
|
||||
contentType: "message/ohttp-res")
|
||||
}
|
||||
|
||||
func target(_ request: URLRequest) -> (Data, HTTPURLResponse) {
|
||||
// Dummy JSON application response
|
||||
let data = try! JSONSerialization.data(withJSONObject: ["hello": "world"])
|
||||
return dataResponse(request: request,
|
||||
body: data,
|
||||
contentType: "application/json")
|
||||
}
|
||||
}
|
||||
|
||||
class OhttpTests: XCTestCase {
|
||||
override func setUp() {
|
||||
OhttpManager.keyCache.removeAll()
|
||||
}
|
||||
|
||||
// Test that a GET request can retrieve expected data from Target, including
|
||||
// passing headers in each direction.
|
||||
func testGet() async {
|
||||
class DataTargetNetwork: FakeOhttpNetwork {
|
||||
override func target(_ request: URLRequest) -> (Data, HTTPURLResponse) {
|
||||
XCTAssertEqual(request.url, URL(string: "https://example.com/data")!)
|
||||
XCTAssertEqual(request.httpMethod, "GET")
|
||||
XCTAssertEqual(request.value(forHTTPHeaderField: "Accept"), "application/octet-stream")
|
||||
|
||||
return dataResponse(request: request,
|
||||
body: Data([0x10, 0x20, 0x30]),
|
||||
contentType: "application/octet-stream")
|
||||
}
|
||||
}
|
||||
|
||||
let mock = DataTargetNetwork()
|
||||
let ohttp = mock.newOhttpManager()
|
||||
|
||||
let url = URL(string: "https://example.com/data")!
|
||||
var request = URLRequest(url: url)
|
||||
request.setValue("application/octet-stream", forHTTPHeaderField: "Accept")
|
||||
let (data, response) = try! await ohttp.data(for: request)
|
||||
|
||||
XCTAssertEqual(response.statusCode, 200)
|
||||
XCTAssertEqual([UInt8](data), [0x10, 0x20, 0x30])
|
||||
XCTAssertEqual(response.value(forHTTPHeaderField: "Content-Type"), "application/octet-stream")
|
||||
}
|
||||
|
||||
// Test that POST requests to an API using JSON work as expected.
|
||||
func testJsonApi() async {
|
||||
class JsonTargetNetwork: FakeOhttpNetwork {
|
||||
override func target(_ request: URLRequest) -> (Data, HTTPURLResponse) {
|
||||
XCTAssertEqual(request.url, URL(string: "https://example.com/api")!)
|
||||
XCTAssertEqual(request.httpMethod, "POST")
|
||||
XCTAssertEqual(request.value(forHTTPHeaderField: "Accept"), "application/json")
|
||||
XCTAssertEqual(request.value(forHTTPHeaderField: "Content-Type"), "application/json")
|
||||
XCTAssertEqual(String(decoding: request.httpBody!, as: UTF8.self),
|
||||
#"{"version":1}"#)
|
||||
|
||||
let data = try! JSONSerialization.data(withJSONObject: ["hello": "world"])
|
||||
return dataResponse(request: request,
|
||||
body: data,
|
||||
contentType: "application/json")
|
||||
}
|
||||
}
|
||||
|
||||
let mock = JsonTargetNetwork()
|
||||
let ohttp = mock.newOhttpManager()
|
||||
|
||||
let url = URL(string: "https://example.com/api")!
|
||||
var request = URLRequest(url: url)
|
||||
request.httpMethod = "POST"
|
||||
request.setValue("application/json", forHTTPHeaderField: "Content-Type")
|
||||
request.setValue("application/json", forHTTPHeaderField: "Accept")
|
||||
request.httpBody = try! JSONSerialization.data(withJSONObject: ["version": 1])
|
||||
let (data, response) = try! await ohttp.data(for: request)
|
||||
|
||||
XCTAssertEqual(response.statusCode, 200)
|
||||
XCTAssertEqual(String(bytes: data, encoding: .utf8), #"{"hello":"world"}"#)
|
||||
XCTAssertEqual(response.value(forHTTPHeaderField: "Content-Type"), "application/json")
|
||||
}
|
||||
|
||||
// Test that config keys are cached across requests.
|
||||
func testKeyCache() async {
|
||||
class CountConfigNetwork: FakeOhttpNetwork {
|
||||
var numConfigFetches = 0
|
||||
|
||||
override func config(_ request: URLRequest) -> (Data, URLResponse) {
|
||||
numConfigFetches += 1
|
||||
return super.config(request)
|
||||
}
|
||||
}
|
||||
let mock = CountConfigNetwork()
|
||||
let ohttp = mock.newOhttpManager()
|
||||
|
||||
let request = URLRequest(url: URL(string: "https://example.com/api")!)
|
||||
_ = try! await ohttp.data(for: request)
|
||||
_ = try! await ohttp.data(for: request)
|
||||
_ = try! await ohttp.data(for: request)
|
||||
|
||||
XCTAssertEqual(mock.numConfigFetches, 1)
|
||||
}
|
||||
|
||||
// Test that bad key config data throws MalformedKeyConfig error.
|
||||
func testBadConfig() async {
|
||||
class MalformedKeyNetwork: FakeOhttpNetwork {
|
||||
override func config(_ request: URLRequest) -> (Data, URLResponse) {
|
||||
dataResponse(request: request,
|
||||
body: Data(),
|
||||
contentType: "application/octet-stream")
|
||||
}
|
||||
}
|
||||
|
||||
do {
|
||||
let mock = MalformedKeyNetwork()
|
||||
let ohttp = mock.newOhttpManager()
|
||||
let request = URLRequest(url: URL(string: "https://example.com/api")!)
|
||||
_ = try await ohttp.data(for: request)
|
||||
XCTFail()
|
||||
} catch OhttpError.MalformedKeyConfig {
|
||||
} catch {
|
||||
XCTFail()
|
||||
}
|
||||
}
|
||||
|
||||
// Test that using the wrong key throws a RelayFailed error and
|
||||
// that the key is removed from cache.
|
||||
func testWrongKey() async {
|
||||
class WrongKeyNetwork: FakeOhttpNetwork {
|
||||
override func config(_ request: URLRequest) -> (Data, URLResponse) {
|
||||
dataResponse(request: request,
|
||||
body: Data(OhttpTestServer().getConfig()),
|
||||
contentType: "application/octet-stream")
|
||||
}
|
||||
|
||||
override func gateway(_ request: URLRequest) -> (Data, URLResponse) {
|
||||
do {
|
||||
_ = try server.receive(message: [UInt8](request.httpBody!))
|
||||
XCTFail()
|
||||
} catch OhttpError.MalformedMessage {
|
||||
} catch {
|
||||
XCTFail()
|
||||
}
|
||||
|
||||
return statusResponse(request: request, statusCode: 400)
|
||||
}
|
||||
}
|
||||
|
||||
do {
|
||||
let mock = WrongKeyNetwork()
|
||||
let ohttp = mock.newOhttpManager()
|
||||
let request = URLRequest(url: URL(string: "https://example.com/")!)
|
||||
_ = try await ohttp.data(for: request)
|
||||
XCTFail()
|
||||
} catch OhttpError.RelayFailed {
|
||||
} catch {
|
||||
XCTFail()
|
||||
}
|
||||
|
||||
XCTAssert(OhttpManager.keyCache.isEmpty)
|
||||
}
|
||||
|
||||
// Test that bad Gateway data generates MalformedMessage errors.
|
||||
func testBadGateway() async {
|
||||
class BadGatewayNetwork: FakeOhttpNetwork {
|
||||
override func gateway(_ request: URLRequest) -> (Data, URLResponse) {
|
||||
dataResponse(request: request,
|
||||
body: Data(),
|
||||
contentType: "message/ohttp-res")
|
||||
}
|
||||
}
|
||||
|
||||
do {
|
||||
let mock = BadGatewayNetwork()
|
||||
let ohttp = mock.newOhttpManager()
|
||||
let request = URLRequest(url: URL(string: "https://example.com/api")!)
|
||||
_ = try await ohttp.data(for: request)
|
||||
XCTFail()
|
||||
} catch OhttpError.MalformedMessage {
|
||||
} catch {
|
||||
XCTFail()
|
||||
}
|
||||
}
|
||||
|
||||
// Test behaviour when Gateway disallows a Target URL.
|
||||
func testDisallowedTarget() async {
|
||||
class DisallowedTargetNetwork: FakeOhttpNetwork {
|
||||
override func target(_ request: URLRequest) -> (Data, HTTPURLResponse) {
|
||||
statusResponse(request: request, statusCode: 403)
|
||||
}
|
||||
}
|
||||
|
||||
let mock = DisallowedTargetNetwork()
|
||||
let ohttp = mock.newOhttpManager()
|
||||
let request = URLRequest(url: URL(string: "https://deny.example.com/")!)
|
||||
let (_, response) = try! await ohttp.data(for: request)
|
||||
|
||||
XCTAssertEqual(response.statusCode, 403)
|
||||
}
|
||||
|
||||
// Test that ordinary network failures are surfaced as URLError.
|
||||
func testNetworkFailure() async {
|
||||
class NoConnectionNetwork: FakeOhttpNetwork {
|
||||
override func client(_ request: URLRequest) async throws -> (Data, URLResponse) {
|
||||
if request.url == configURL {
|
||||
return config(request)
|
||||
}
|
||||
|
||||
throw NSError(domain: NSURLErrorDomain,
|
||||
code: URLError.cannotConnectToHost.rawValue)
|
||||
}
|
||||
}
|
||||
|
||||
do {
|
||||
let mock = NoConnectionNetwork()
|
||||
let ohttp = mock.newOhttpManager()
|
||||
let request = URLRequest(url: URL(string: "https://example.com/api")!)
|
||||
_ = try await ohttp.data(for: request)
|
||||
XCTFail()
|
||||
} catch is URLError {
|
||||
} catch {
|
||||
XCTFail()
|
||||
}
|
||||
}
|
||||
}
|
|
@ -168,6 +168,7 @@ if [ -z $IS_FOCUS ]; then
|
|||
$CARGO uniffi-bindgen generate "$REPO_ROOT/components/places/src/places.udl" -l swift -o "$COMMON/Headers"
|
||||
$CARGO uniffi-bindgen generate "$REPO_ROOT/components/sync_manager/src/syncmanager.udl" -l swift -o "$COMMON/Headers"
|
||||
$CARGO uniffi-bindgen generate "$REPO_ROOT/components/sync15/src/sync15.udl" -l swift -o "$COMMON/Headers"
|
||||
$CARGO uniffi-bindgen generate "$REPO_ROOT/components/as-ohttp-client/src/as_ohttp_client.udl" -l swift -o "$COMMON/Headers"
|
||||
fi
|
||||
rm -rf "$COMMON"/Headers/*.swift
|
||||
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
#![allow(unknown_lints)]
|
||||
#![warn(rust_2018_idioms)]
|
||||
|
||||
pub use as_ohttp_client;
|
||||
pub use autofill;
|
||||
pub use crashtest;
|
||||
pub use error_support;
|
||||
|
|
|
@ -10,6 +10,7 @@ import os
|
|||
ROOT_DIR = pathlib.Path(__file__).parent.parent.parent
|
||||
# List of udl_paths to generate bindings for
|
||||
BINDINGS_UDL_PATHS = [
|
||||
"components/as-ohttp-client/src/as_ohttp_client.udl",
|
||||
"components/autofill/src/autofill.udl",
|
||||
"components/fxa-client/src/fxa_client.udl",
|
||||
"components/logins/src/logins.udl",
|
||||
|
@ -32,6 +33,7 @@ FOCUS_UDL_PATHS = [
|
|||
|
||||
# List of globs to copy the sources from
|
||||
SOURCE_TO_COPY = [
|
||||
"components/as-ohttp-client/ios/ASOhttpClient",
|
||||
"components/nimbus/ios/Nimbus",
|
||||
"components/fxa-client/ios/FxAClient",
|
||||
"components/logins/ios/Logins",
|
||||
|
|
|
@ -227,7 +227,7 @@ PACKAGE_METADATA_FIXUPS = {
|
|||
"fixup": "https://raw.githubusercontent.com/tokio-rs/tracing/master/LICENSE"
|
||||
}
|
||||
},
|
||||
# These packages do not unambiguously delcare their licensing file.
|
||||
# These packages do not unambiguously declare their licensing file.
|
||||
"publicsuffix": {
|
||||
"license": {
|
||||
"check": "MIT/Apache-2.0"
|
||||
|
@ -264,6 +264,51 @@ PACKAGE_METADATA_FIXUPS = {
|
|||
"fixup": "LICENSE-APACHE",
|
||||
}
|
||||
},
|
||||
"ohttp": {
|
||||
"license": {
|
||||
"check": "MIT OR Apache-2.0"
|
||||
},
|
||||
"license_file": {
|
||||
"check": None,
|
||||
"fixup": "https://raw.githubusercontent.com/martinthomson/ohttp/main/LICENSE-APACHE",
|
||||
}
|
||||
},
|
||||
"bhttp": {
|
||||
"license": {
|
||||
"check": "MIT OR Apache-2.0"
|
||||
},
|
||||
"license_file": {
|
||||
"check": None,
|
||||
"fixup": "https://raw.githubusercontent.com/martinthomson/ohttp/main/LICENSE-APACHE",
|
||||
}
|
||||
},
|
||||
"zeroize_derive": {
|
||||
"license": {
|
||||
"check": "Apache-2.0 OR MIT"
|
||||
},
|
||||
"license_file": {
|
||||
"check": None,
|
||||
"fixup": "https://raw.githubusercontent.com/RustCrypto/utils/master/zeroize/derive/LICENSE-APACHE"
|
||||
}
|
||||
},
|
||||
"ctr": {
|
||||
"license": {
|
||||
"check": "MIT OR Apache-2.0"
|
||||
},
|
||||
"license_file": {
|
||||
"check": None,
|
||||
"fixup": "https://raw.githubusercontent.com/RustCrypto/block-modes/master/ctr/LICENSE-APACHE",
|
||||
}
|
||||
},
|
||||
"crypto-mac": {
|
||||
"license": {
|
||||
"check": "MIT OR Apache-2.0"
|
||||
},
|
||||
"license_file": {
|
||||
"check": None,
|
||||
"fixup": "https://raw.githubusercontent.com/RustCrypto/traits/master/digest/LICENSE-APACHE",
|
||||
}
|
||||
},
|
||||
# These packages do not include their license file in their release distributions,
|
||||
# so we have to fetch it over the network. Each has been manually checked and resolved
|
||||
# to a final URL from which the file can be fetched (typically based on the *name* of
|
||||
|
|
Загрузка…
Ссылка в новой задаче