diff --git a/Cargo.lock b/Cargo.lock index 4e974e0480f..263bbbd105c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -218,6 +218,45 @@ version = "0.7.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d3fb67a6e08acf24fdeccbac2cb6ac4305825bd1f117462e0e6f2f193345ad56" +[[package]] +name = "asn1-rs" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7f43a50ac4fdca5df8e885c21b835997f0a1cdee65494a6847694a98652d9d8" +dependencies = [ + "asn1-rs-derive", + "asn1-rs-impl", + "displaydoc", + "nom", + "num-traits", + "rusticata-macros", + "thiserror 2.0.18", + "time", +] + +[[package]] +name = "asn1-rs-derive" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3109e49b1e4909e9db6515a30c633684d68cdeaa252f215214cb4fa1a5bfee2c" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", + "synstructure", +] + +[[package]] +name = "asn1-rs-impl" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b18050c2cd6fe86c3a76584ef5e0baf286d038cda203eb6223df2cc413565f7" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + [[package]] name = "atomic-waker" version = "1.1.2" @@ -230,12 +269,6 @@ version = "1.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f2032f911046de80f0a198e0901378627c33f59ea0ac00e363d481118bd70a53" -[[package]] -name = "base16ct" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c7f02d4ea65f2c1853089ffd8d2787bdbc63de2f0d29dedbcf8ccdfa0ccd4cf" - [[package]] name = "base16ct" version = "1.0.0" @@ -278,6 +311,15 @@ dependencies = [ "sha2 0.10.9", ] +[[package]] +name = "bit-vec" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b71798fca2c1fe1086445a7258a4bc81e6e49dcd24c8d0dd9a1e57395b603f51" +dependencies = [ + "serde", +] + [[package]] name = "bit_field" version = "0.10.2" @@ -547,19 +589,6 @@ dependencies = [ "rand_core 0.10.0", ] -[[package]] -name = "chacha20poly1305" -version = "0.10.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "10cd79432192d1c0f4e1a0fef9527696cc039165d729fb41b3f4f4f354c2dc35" -dependencies = [ - "aead 0.5.2", - "chacha20 0.9.1", - "cipher 0.4.4", - "poly1305", - "zeroize", -] - [[package]] name = "chrono" version = "0.4.44" @@ -581,7 +610,6 @@ checksum = "773f3b9af64447d2ce9850330c473515014aa235e6a783b02db81ff39e4a3dad" dependencies = [ "crypto-common 0.1.7", "inout 0.1.4", - "zeroize", ] [[package]] @@ -822,18 +850,6 @@ version = "0.8.23" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a31eee39dddec8330830986fcd7625edb5a24ec90ea038215273bbc3adb08ac6" -[[package]] -name = "crypto-bigint" -version = "0.5.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0dc92fb57ca44df6db8059111ab3af99a63d5d0f8375d9972e319a379c6bab76" -dependencies = [ - "generic-array 0.14.7", - "rand_core 0.6.4", - "subtle", - "zeroize", -] - [[package]] name = "crypto-bigint" version = "0.7.3" @@ -879,7 +895,7 @@ version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "21f41f23de7d24cdbda7f0c4d9c0351f99a4ceb258ef30e5c1927af8987ffe5a" dependencies = [ - "crypto-bigint 0.7.3", + "crypto-bigint", "libm", "rand_core 0.10.0", ] @@ -931,7 +947,6 @@ dependencies = [ "fiat-crypto 0.2.9", "rustc_version", "subtle", - "zeroize", ] [[package]] @@ -1046,26 +1061,35 @@ dependencies = [ [[package]] name = "der" -version = "0.7.10" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7c1832837b905bbfb5101e07cc24c8deddf52f93225eee6ead5f4d63d53ddcb" +checksum = "71fd89660b2dc699704064e59e9dba0147b903e85319429e131620d022be411b" dependencies = [ - "const-oid 0.9.6", - "pem-rfc7468 0.7.0", + "const-oid 0.10.2", + "pem-rfc7468 1.0.0", "zeroize", ] [[package]] -name = "der" -version = "0.8.0" +name = "der-parser" +version = "10.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "71fd89660b2dc699704064e59e9dba0147b903e85319429e131620d022be411b" +checksum = "07da5016415d5a3c4dd39b11ed26f915f52fc4e0dc197d87908bc916e51bc1a6" dependencies = [ - "const-oid 0.10.2", - "pem-rfc7468 1.0.0", - "zeroize", + "asn1-rs", + "displaydoc", + "nom", + "num-bigint", + "num-traits", + "rusticata-macros", ] +[[package]] +name = "deranged" +version = "0.5.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7cd812cc2bc1d69d4764bd80df88b4317eaef9e773c75226407d9bc0876b211c" + [[package]] name = "derive-into-owned" version = "0.2.0" @@ -1128,6 +1152,17 @@ dependencies = [ "objc2 0.6.4", ] +[[package]] +name = "displaydoc" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c6232dd377dcc64799954cbd3a9bb882e9cdc1308ccd87b1c098f1fb2eaf82a8" +dependencies = [ + "proc-macro2", + "quote", + "syn 3.0.6", +] + [[package]] name = "dlib" version = "0.5.3" @@ -1155,12 +1190,10 @@ dependencies = [ "cc", "cpal", "flate2", - "rustls-rustcrypto", "rustysynth", "softbuffer", "tar", "ureq", - "webpki-roots", "winit", ] @@ -1181,32 +1214,18 @@ version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "92773504d58c093f6de2459af4af33faa518c13451eb8f2b5698ed3d36e7c813" -[[package]] -name = "ecdsa" -version = "0.16.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee27f32b5c5292967d2d4a9d7f1e0b0aed2c15daded5a60300e4abb9d8020bca" -dependencies = [ - "der 0.7.10", - "digest 0.10.7", - "elliptic-curve 0.13.8", - "rfc6979 0.4.0", - "signature 2.2.0", - "spki 0.7.3", -] - [[package]] name = "ecdsa" version = "0.17.0-rc.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "91bbdd377139884fafcad8dc43a760a3e1e681aa26db910257fa6535b70e1829" dependencies = [ - "der 0.8.0", + "der", "digest 0.11.1", - "elliptic-curve 0.14.0-rc.29", - "rfc6979 0.5.0-rc.5", + "elliptic-curve", + "rfc6979", "signature 3.0.0-rc.10", - "spki 0.8.0-rc.4", + "spki", "zeroize", ] @@ -1216,7 +1235,6 @@ version = "2.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "115531babc129696a58c64a4fef0a8bf9e9698629fb97e9e40767d235cfbcd53" dependencies = [ - "pkcs8 0.10.2", "signature 2.2.0", ] @@ -1226,7 +1244,7 @@ version = "3.0.0-rc.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c6e914c7c52decb085cea910552e24c63ac019e3ab8bf001ff736da9a9d9d890" dependencies = [ - "pkcs8 0.11.0-rc.11", + "pkcs8", "signature 3.0.0-rc.10", ] @@ -1238,10 +1256,8 @@ checksum = "70e796c081cee67dc755e1a36a0a172b897fab85fc3f6bc48307991f64e4eca9" dependencies = [ "curve25519-dalek 4.1.3", "ed25519 2.2.3", - "serde", "sha2 0.10.9", "subtle", - "zeroize", ] [[package]] @@ -1269,46 +1285,25 @@ dependencies = [ "toyos-window", ] -[[package]] -name = "elliptic-curve" -version = "0.13.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5e6043086bf7973472e0c7dff2142ea0b680d30e18d9cc40f267efbf222bd47" -dependencies = [ - "base16ct 0.2.0", - "crypto-bigint 0.5.5", - "digest 0.10.7", - "ff", - "generic-array 0.14.7", - "group", - "hkdf 0.12.4", - "pem-rfc7468 0.7.0", - "pkcs8 0.10.2", - "rand_core 0.6.4", - "sec1 0.7.3", - "subtle", - "zeroize", -] - [[package]] name = "elliptic-curve" version = "0.14.0-rc.29" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e84043d573efd4ac9d2d125817979a379204bf7e328b25a4a30487e8d100e618" dependencies = [ - "base16ct 1.0.0", - "crypto-bigint 0.7.3", + "base16ct", + "crypto-bigint", "crypto-common 0.2.1", "digest 0.11.1", - "hkdf 0.13.0-rc.5", + "hkdf", "hybrid-array", "once_cell", "pem-rfc7468 1.0.0", - "pkcs8 0.11.0-rc.11", + "pkcs8", "rand_core 0.10.0", "rustcrypto-ff", "rustcrypto-group", - "sec1 0.8.1", + "sec1", "subtle", "zeroize", ] @@ -1412,16 +1407,6 @@ dependencies = [ "simd-adler32", ] -[[package]] -name = "ff" -version = "0.13.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0b50bfb653653f9ca9095b427bed08ab8d75a137839d9ad64eb11810d5b6393" -dependencies = [ - "rand_core 0.6.4", - "subtle", -] - [[package]] name = "fiat-crypto" version = "0.2.9" @@ -1673,7 +1658,6 @@ checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" dependencies = [ "typenum", "version_check", - "zeroize", ] [[package]] @@ -2093,7 +2077,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "305657ad44356151d73c145f567c25b24fdf2466bfb3e90727e1d5461a4e9748" dependencies = [ "bstr", - "hashbrown 0.16.1", + "hashbrown 0.17.1", ] [[package]] @@ -2566,17 +2550,6 @@ dependencies = [ "uuid", ] -[[package]] -name = "group" -version = "0.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0f9ef7462f7c099f518d754361858f86d8a07af53ba9af0fe635bbccb151a63" -dependencies = [ - "ff", - "rand_core 0.6.4", - "subtle", -] - [[package]] name = "hash32" version = "0.3.1" @@ -2659,15 +2632,6 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e712f64ec3850b98572bffac52e2c6f282b29fe6c5fa6d42334b30be438d95c1" -[[package]] -name = "hkdf" -version = "0.12.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b5f8eb2ad728638ea2c7d47a21db23b7b58a72ed6a38256b8a1849f15fbbdf7" -dependencies = [ - "hmac 0.12.1", -] - [[package]] name = "hkdf" version = "0.13.0-rc.5" @@ -2826,18 +2790,18 @@ checksum = "25f8a978272e3cbdf4768f7363eb1c8e1e6ba63c52a3ed05e29e222da4aec7cb" dependencies = [ "argon2", "bcrypt-pbkdf", - "crypto-bigint 0.7.3", - "ecdsa 0.17.0-rc.16", + "crypto-bigint", + "ecdsa", "ed25519-dalek 3.0.0-pre.6", "hex", "hmac 0.13.0-rc.5", "num-bigint-dig", - "p256 0.14.0-rc.8", - "p384 0.14.0-rc.8", + "p256", + "p384", "p521", "rand_core 0.10.0", - "rsa 0.10.0-rc.17", - "sec1 0.8.1", + "rsa", + "sec1", "sha1 0.11.0-rc.5", "sha2 0.11.0-rc.5", "signature 3.0.0-rc.10", @@ -3250,6 +3214,12 @@ dependencies = [ "toyos-tco", ] +[[package]] +name = "minimal-lexical" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" + [[package]] name = "miniz_oxide" version = "0.8.9" @@ -3386,6 +3356,16 @@ dependencies = [ "libc", ] +[[package]] +name = "nom" +version = "7.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" +dependencies = [ + "memchr", + "minimal-lexical", +] + [[package]] name = "nonempty" version = "0.12.0" @@ -3401,6 +3381,16 @@ dependencies = [ "windows-sys 0.61.2", ] +[[package]] +name = "num-bigint" +version = "0.4.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c89e69e7e0f03bea5ef08013795c25018e101932225a656383bd384495ecc367" +dependencies = [ + "num-integer", + "num-traits", +] + [[package]] name = "num-bigint-dig" version = "0.8.6" @@ -3415,7 +3405,6 @@ dependencies = [ "rand 0.8.5", "serde", "smallvec", - "zeroize", ] [[package]] @@ -3427,6 +3416,12 @@ dependencies = [ "num-traits", ] +[[package]] +name = "num-conv" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "521739c6d2bac4aa25192232afe6841231376b2b26d4d9fae5ecf8ca5772e441" + [[package]] name = "num-derive" version = "0.4.2" @@ -3465,7 +3460,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" dependencies = [ "autocfg", - "libm", ] [[package]] @@ -3822,6 +3816,15 @@ dependencies = [ "memchr", ] +[[package]] +name = "oid-registry" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "12f40cff3dde1b6087cc5d5f5d4d65712f34016a03ed60e9c08dcc392736b5b7" +dependencies = [ + "asn1-rs", +] + [[package]] name = "once_cell" version = "1.21.4" @@ -3844,54 +3847,30 @@ dependencies = [ "libredox", ] -[[package]] -name = "p256" -version = "0.13.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c9863ad85fa8f4460f9c48cb909d38a0d689dba1f6f6988a5e3e0d31071bcd4b" -dependencies = [ - "ecdsa 0.16.9", - "elliptic-curve 0.13.8", - "primeorder 0.13.6", - "sha2 0.10.9", -] - [[package]] name = "p256" version = "0.14.0-rc.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "44f0a10fe314869359cb2901342b045f4e5a962ef9febc006f03d2a8c848fe4c" dependencies = [ - "ecdsa 0.17.0-rc.16", - "elliptic-curve 0.14.0-rc.29", + "ecdsa", + "elliptic-curve", "primefield", - "primeorder 0.14.0-rc.8", + "primeorder", "sha2 0.11.0-rc.5", ] -[[package]] -name = "p384" -version = "0.13.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe42f1670a52a47d448f14b6a5c61dd78fce51856e68edaa38f7ae3a46b8d6b6" -dependencies = [ - "ecdsa 0.16.9", - "elliptic-curve 0.13.8", - "primeorder 0.13.6", - "sha2 0.10.9", -] - [[package]] name = "p384" version = "0.14.0-rc.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b079e66810c55ab3d6ba424e056dc4aefcdb8046c8c3f3816142edbdd7af7721" dependencies = [ - "ecdsa 0.17.0-rc.16", - "elliptic-curve 0.14.0-rc.29", + "ecdsa", + "elliptic-curve", "fiat-crypto 0.3.0", "primefield", - "primeorder 0.14.0-rc.8", + "primeorder", "sha2 0.11.0-rc.5", ] @@ -3901,11 +3880,11 @@ version = "0.14.0-rc.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9eecc34c4c6e6596d5271fecf90ac4f16593fa198e77282214d0c22736aa9266" dependencies = [ - "base16ct 1.0.0", - "ecdsa 0.17.0-rc.16", - "elliptic-curve 0.14.0-rc.29", + "base16ct", + "ecdsa", + "elliptic-curve", "primefield", - "primeorder 0.14.0-rc.8", + "primeorder", "sha2 0.11.0-rc.5", ] @@ -3970,12 +3949,6 @@ dependencies = [ "subtle", ] -[[package]] -name = "paste" -version = "1.0.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" - [[package]] name = "pbkdf2" version = "0.12.2" @@ -4007,6 +3980,16 @@ dependencies = [ "thiserror 1.0.69", ] +[[package]] +name = "pem" +version = "4.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d354a98a3d1251555de99e8fdd8afda05573c31b82f59063a7b0a29b5527f120" +dependencies = [ + "base64 0.23.1", + "serde_core", +] + [[package]] name = "pem-rfc7468" version = "0.7.0" @@ -4063,35 +4046,14 @@ version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd" -[[package]] -name = "pkcs1" -version = "0.7.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8ffb9f10fa047879315e6625af03c164b16962a5368d724ed16323b68ace47f" -dependencies = [ - "der 0.7.10", - "pkcs8 0.10.2", - "spki 0.7.3", -] - [[package]] name = "pkcs1" version = "0.8.0-rc.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "986d2e952779af96ea048f160fd9194e1751b4faea78bcf3ceb456efe008088e" dependencies = [ - "der 0.8.0", - "spki 0.8.0-rc.4", -] - -[[package]] -name = "pkcs5" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e847e2c91a18bfa887dd028ec33f2fe6f25db77db3619024764914affe8b69a6" -dependencies = [ - "der 0.7.10", - "spki 0.7.3", + "der", + "spki", ] [[package]] @@ -4103,23 +4065,12 @@ dependencies = [ "aes 0.9.0-rc.4", "aes-gcm 0.11.0-rc.3", "cbc 0.2.0-rc.4", - "der 0.8.0", + "der", "pbkdf2 0.13.0-rc.9", "rand_core 0.10.0", "scrypt", "sha2 0.11.0-rc.5", - "spki 0.8.0-rc.4", -] - -[[package]] -name = "pkcs8" -version = "0.10.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f950b2377845cebe5cf8b5165cb3cc1a5e0fa5cfa3e1f7f55707d8fd82e0a7b7" -dependencies = [ - "der 0.7.10", - "pkcs5 0.7.1", - "spki 0.7.3", + "spki", ] [[package]] @@ -4128,10 +4079,10 @@ version = "0.11.0-rc.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "12922b6296c06eb741b02d7b5161e3aaa22864af38dfa025a1a3ba3f68c84577" dependencies = [ - "der 0.8.0", - "pkcs5 0.8.0-rc.13", + "der", + "pkcs5", "rand_core 0.10.0", - "spki 0.8.0-rc.4", + "spki", ] [[package]] @@ -4232,6 +4183,12 @@ dependencies = [ "portable-atomic", ] +[[package]] +name = "powerfmt" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a6394b9e965e73d0a289ee54f589087e2c676aedf60885baf52c76b771e4958" + [[package]] name = "ppv-lite86" version = "0.2.21" @@ -4266,7 +4223,7 @@ version = "0.14.0-rc.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c6543f5eec854fbf74ba5ef651fbdc9408919b47c3e1526623687135c16d12e9" dependencies = [ - "crypto-bigint 0.7.3", + "crypto-bigint", "crypto-common 0.2.1", "rand_core 0.10.0", "rustcrypto-ff", @@ -4274,22 +4231,13 @@ dependencies = [ "zeroize", ] -[[package]] -name = "primeorder" -version = "0.13.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "353e1ca18966c16d9deb1c69278edbc5f194139612772bd9537af60ac231e1e6" -dependencies = [ - "elliptic-curve 0.13.8", -] - [[package]] name = "primeorder" version = "0.14.0-rc.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "569d9ad6ef822bb0322c7e7d84e5e286244050bd5246cac4c013535ae91c2c90" dependencies = [ - "elliptic-curve 0.14.0-rc.29", + "elliptic-curve", ] [[package]] @@ -4460,6 +4408,20 @@ name = "raw-window-handle" version = "0.6.2" source = "git+https://github.com/ToyOSOrg/raw-window-handle?branch=toyos-0.6.2#2a5d102bdbd5dc987b653a7ffff5bedbbbfc53b3" +[[package]] +name = "rcgen" +version = "0.14.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8774e05a7d0de114588e6a28fe7e71694b82614ed569d86d8b389dfbc98b8ad8" +dependencies = [ + "pem", + "ring", + "rustls-pki-types", + "time", + "x509-parser", + "yasna", +] + [[package]] name = "realfft" version = "3.5.0" @@ -4527,16 +4489,6 @@ dependencies = [ "usvg", ] -[[package]] -name = "rfc6979" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8dd2a808d456c4a54e300a23e9f5a67e122c3024119acbfd73e3bf664491cb2" -dependencies = [ - "hmac 0.12.1", - "subtle", -] - [[package]] name = "rfc6979" version = "0.5.0-rc.5" @@ -4559,8 +4511,7 @@ dependencies = [ [[package]] name = "ring" version = "0.17.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4689e6c2294d81e88dc6261c768b63bc4fcdb852be6d1352498b114f61383b7" +source = "git+https://github.com/ToyOSOrg/ring?branch=toyos-0.17.14#34818c4599e7d6233cf42e80de5cd309190e523c" dependencies = [ "cc", "cfg-if", @@ -4579,27 +4530,6 @@ dependencies = [ "memchr", ] -[[package]] -name = "rsa" -version = "0.9.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8573f03f5883dcaebdfcf4725caa1ecb9c15b2ef50c43a07b816e06799bb12d" -dependencies = [ - "const-oid 0.9.6", - "digest 0.10.7", - "num-bigint-dig", - "num-integer", - "num-traits", - "pkcs1 0.7.5", - "pkcs8 0.10.2", - "rand_core 0.6.4", - "sha2 0.10.9", - "signature 2.2.0", - "spki 0.7.3", - "subtle", - "zeroize", -] - [[package]] name = "rsa" version = "0.10.0-rc.17" @@ -4607,15 +4537,15 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "87ed3e93fc7e473e464b9726f4759659e72bc8665e4b8ea227547024f416d905" dependencies = [ "const-oid 0.10.2", - "crypto-bigint 0.7.3", + "crypto-bigint", "crypto-primes", "digest 0.11.1", - "pkcs1 0.8.0-rc.4", - "pkcs8 0.11.0-rc.11", + "pkcs1", + "pkcs8", "rand_core 0.10.0", "sha2 0.11.0-rc.5", "signature 3.0.0-rc.10", - "spki 0.8.0-rc.4", + "spki", "zeroize", ] @@ -4645,16 +4575,16 @@ dependencies = [ "cbc 0.1.2", "chacha20 0.9.1", "cipher 0.5.1", - "crypto-bigint 0.7.3", + "crypto-bigint", "ctr 0.9.2", "curve25519-dalek 5.0.0-pre.6", "data-encoding", "delegate", - "der 0.8.0", + "der", "digest 0.10.7", - "ecdsa 0.17.0-rc.16", + "ecdsa", "ed25519-dalek 3.0.0-pre.6", - "elliptic-curve 0.14.0-rc.29", + "elliptic-curve", "enum_dispatch", "flate2", "futures", @@ -4669,26 +4599,26 @@ dependencies = [ "md5", "ml-kem", "module-lattice", - "p256 0.14.0-rc.8", - "p384 0.14.0-rc.8", + "p256", + "p384", "p521", "pageant", "pbkdf2 0.12.2", - "pkcs1 0.8.0-rc.4", - "pkcs5 0.8.0-rc.13", - "pkcs8 0.11.0-rc.11", + "pkcs1", + "pkcs5", + "pkcs8", "poly1305", "polyval 0.7.1", "rand 0.10.0", "rand_core 0.10.0", - "rsa 0.10.0-rc.17", + "rsa", "russh-cryptovec", "russh-util", - "sec1 0.8.1", + "sec1", "sha1 0.10.6", "sha2 0.10.9", "signature 3.0.0-rc.10", - "spki 0.8.0-rc.4", + "spki", "ssh-encoding", "subtle", "thiserror 2.0.18", @@ -4776,6 +4706,15 @@ dependencies = [ "transpose", ] +[[package]] +name = "rusticata-macros" +version = "4.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "faf0c4a6ece9950b9abdb62b1cfcf2a68b3b67a10ba445b3bb85be2a293d0632" +dependencies = [ + "nom", +] + [[package]] name = "rustix" version = "0.38.44" @@ -4812,7 +4751,7 @@ dependencies = [ "once_cell", "ring", "rustls-pki-types", - "rustls-webpki 0.103.15", + "rustls-webpki", "subtle", "zeroize", ] @@ -4826,47 +4765,6 @@ dependencies = [ "zeroize", ] -[[package]] -name = "rustls-rustcrypto" -version = "0.0.2-alpha" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f12052947763ab8515f753315357599e9b0b4dab3b8ba15f30f725fe6d025557" -dependencies = [ - "aead 0.5.2", - "aes-gcm 0.10.3", - "chacha20poly1305", - "crypto-common 0.1.7", - "der 0.7.10", - "digest 0.10.7", - "ecdsa 0.16.9", - "ed25519-dalek 2.2.0", - "hmac 0.12.1", - "p256 0.13.2", - "p384 0.13.1", - "paste", - "pkcs8 0.10.2", - "rand_core 0.6.4", - "rsa 0.9.10", - "rustls", - "rustls-pki-types", - "rustls-webpki 0.102.8", - "sec1 0.7.3", - "sha2 0.10.9", - "signature 2.2.0", - "x25519-dalek", -] - -[[package]] -name = "rustls-webpki" -version = "0.102.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64ca1bc8749bd4cf37b5ce386cc146580777b4e8572c7b97baf22c83f444bee9" -dependencies = [ - "ring", - "rustls-pki-types", - "untrusted", -] - [[package]] name = "rustls-webpki" version = "0.103.15" @@ -4933,29 +4831,15 @@ dependencies = [ "sha2 0.11.0-rc.5", ] -[[package]] -name = "sec1" -version = "0.7.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3e97a565f76233a6003f9f5c54be1d9c5bdfa3eccfb189469f11ec4901c47dc" -dependencies = [ - "base16ct 0.2.0", - "der 0.7.10", - "generic-array 0.14.7", - "pkcs8 0.10.2", - "subtle", - "zeroize", -] - [[package]] name = "sec1" version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d56d437c2f19203ce5f7122e507831de96f3d2d4d3be5af44a0b0a09d8a80e4d" dependencies = [ - "base16ct 1.0.0", + "base16ct", "ctutils", - "der 0.8.0", + "der", "hybrid-array", "subtle", "zeroize", @@ -5025,7 +4909,7 @@ version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9af4a3e75ebd5599b30d4de5768e00b5095d518a79fefc3ecbaf77e665d1ec06" dependencies = [ - "base16ct 1.0.0", + "base16ct", "serde", ] @@ -5117,10 +5001,6 @@ name = "signature" version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "77549399552de45a898a580c1b41d445bf730df867cc44e6c0233bbc4b8329de" -dependencies = [ - "digest 0.10.7", - "rand_core 0.6.4", -] [[package]] name = "signature" @@ -5283,16 +5163,6 @@ version = "0.9.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" -[[package]] -name = "spki" -version = "0.7.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d91ed6c858b01f942cd56b37a94b3e0a1798290327d1236e4d9cf4eaca44d29d" -dependencies = [ - "base64ct", - "der 0.7.10", -] - [[package]] name = "spki" version = "0.8.0-rc.4" @@ -5300,7 +5170,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8baeff88f34ed0691978ec34440140e1572b68c7dd4a495fd14a3dc1944daa80" dependencies = [ "base64ct", - "der 0.8.0", + "der", ] [[package]] @@ -5451,6 +5321,17 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "synstructure" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + [[package]] name = "tar" version = "0.4.46" @@ -5469,7 +5350,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32497e9a4c7b38532efcdebeef879707aa9f794296a4f0244f6f69e9bc8574bd" dependencies = [ "fastrand", - "getrandom 0.3.4", + "getrandom 0.4.2", "once_cell", "rustix 1.1.5", "windows-sys 0.61.2", @@ -5542,6 +5423,36 @@ dependencies = [ "cfg-if", ] +[[package]] +name = "time" +version = "0.3.55" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cdb87b95ec50ddfa440816d227a17b2ccbdda963a316a727fda0fc4334f7d134" +dependencies = [ + "deranged", + "num-conv", + "powerfmt", + "serde_core", + "time-core", + "time-macros", +] + +[[package]] +name = "time-core" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e1c906769ad99c88eaa54e728060edef082f8e358ff32030cb7c7d315e81109" + +[[package]] +name = "time-macros" +version = "0.2.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e689342a48d2ea927c87ea50cabf8594854bf940e9310208848d680d668ed85" +dependencies = [ + "num-conv", + "time-core", +] + [[package]] name = "tiny-skia" version = "0.12.0" @@ -5749,6 +5660,9 @@ dependencies = [ "image", "kernel", "libc", + "pem", + "rcgen", + "rustls", "serde", "serde_json", "sha2 0.10.9", @@ -5775,6 +5689,7 @@ dependencies = [ "toyos-xhci", "ureq", "uuid", + "webpki-root-certs", ] [[package]] @@ -6668,6 +6583,15 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "webpki-root-certs" +version = "1.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b96554aa2acc8ccdb7e1c9a58a7a68dd5d13bccc69cd124cb09406db612a1c9b" +dependencies = [ + "rustls-pki-types", +] + [[package]] name = "webpki-roots" version = "1.0.9" @@ -7089,14 +7013,21 @@ dependencies = [ ] [[package]] -name = "x25519-dalek" -version = "2.0.1" +name = "x509-parser" +version = "0.18.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c7e468321c81fb07fa7f4c636c3972b9100f0346e5b6a9f2bd0603a52f7ed277" +checksum = "d43b0f71ce057da06bc0851b23ee24f3f86190b07203dd8f567d0b706a185202" dependencies = [ - "curve25519-dalek 4.1.3", - "rand_core 0.6.4", - "zeroize", + "asn1-rs", + "data-encoding", + "der-parser", + "lazy_static", + "nom", + "oid-registry", + "ring", + "rusticata-macros", + "thiserror 2.0.18", + "time", ] [[package]] @@ -7140,6 +7071,16 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ec7a2a501ed189703dba8b08142f057e887dfc4b2cc4db2d343ac6376ba3e0b9" +[[package]] +name = "yasna" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5f6765e852b9b4dc8e2a76843e4d64d1cea8e79bcde0b6901aea8e7c7f08282" +dependencies = [ + "bit-vec", + "time", +] + [[package]] name = "zerocopy" version = "0.8.47" @@ -7165,20 +7106,6 @@ name = "zeroize" version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e13c156562582aa81c60cb29407084cdb54c4164760106ab78e6c5b0858cf64e" -dependencies = [ - "zeroize_derive", -] - -[[package]] -name = "zeroize_derive" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c50655cbb0fe3fc43170059e702f1ce5e19b84cec58dc87b037a09935c2f328" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.117", -] [[package]] name = "zlib-rs" diff --git a/Cargo.toml b/Cargo.toml index 983504f7503..dfc0b15e1d7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -218,6 +218,10 @@ ureq = { version = "3", default-features = false, features = ["rustls"] } # The commit an image is built from, its time and whether the tree is that # commit's, where `git` is a host's C tool (`src/build.rs`'s `release`). gix = { version = "0.88", default-features = false, features = ["status", "sha1"] } +# The authorities `build::trust_roots` writes to ROOT, as the rustls project +# publishes the Mozilla root program, and the PEM it writes them in. +webpki-root-certs = "1" +pem = "4" [dev-dependencies] # `USER_TOP`, so the harness judges a held `rsp` against the bound the kernel @@ -232,6 +236,10 @@ toyos-xhci = { path = "toyos-xhci" } # The ACPI server's count line, so the judge reads the words the server writes # and the job that waits for them matches. acpiserver-api = { path = "userland/acpiserver-api" } +# `common::https`'s server, the peer a guest's HTTPS client is judged +# against, and the authority and certificates it presents, made per run. +rustls = { version = "0.23", default-features = false, features = ["ring", "std", "tls12"] } +rcgen = "0.14" [patch.crates-io] loom = { git = "https://github.com/ToyOSOrg/loom", branch = "toyos" } @@ -254,6 +262,7 @@ raw-window-handle = { git = "https://github.com/ToyOSOrg/raw-window-handle", bra cpal = { git = "https://github.com/ToyOSOrg/cpal", branch = "toyos-0.18.0" } softbuffer = { git = "https://github.com/ToyOSOrg/softbuffer", branch = "toyos-0.4.8" } winit = { git = "https://github.com/ToyOSOrg/winit", branch = "toyos-0.30.13" } +ring = { git = "https://github.com/ToyOSOrg/ring", branch = "toyos-0.17.14" } # The one profile every guest binary is built with. # Optimised, because an unoptimised guest mismeasures everything under TCG; diff --git a/NOTICE b/NOTICE index 224294bec76..dae60737d72 100644 --- a/NOTICE +++ b/NOTICE @@ -244,6 +244,20 @@ rust-lang/llvm-project, Apache-2.0 WITH LLVM-exception like upstream's; the toolchain release carries the clang, `lld` and LLVM tools built from it. +licenses/CDLA-Permissive-2.0-webpki-root-certs.txt — the Mozilla roots, CDLA-Permissive-2.0 +------------------------------------------------------------------------------------------- + +Every image carries the authorities of Mozilla's root program as the +`webpki-root-certs` crate publishes them, a derived work of the CCADB's data, +written by `src/build.rs`'s `trust_roots`. That crate is under +CDLA-Permissive-2.0, which the owner allowed for these roots: it asks that the +agreement's text travel with the data, and the image carries it beside them as +/system/etc/ssl/CDLA-Permissive-2.0.txt. + + Licence text: licenses/CDLA-Permissive-2.0-webpki-root-certs.txt (the + crate's `LICENSE`, sha256 + e271993808fec50ab29350b39539cdec611a9103f827e0aa26d61da70e2d33f8) + bcachefs/tests/fixtures/ — volumes upstream's tools wrote --------------------------------------------------------- diff --git a/issues/a-guest-close-after-a-refused-handshake-can-leave-its-peer-waiting.md b/issues/a-guest-close-after-a-refused-handshake-can-leave-its-peer-waiting.md new file mode 100644 index 00000000000..eb8a700df05 --- /dev/null +++ b/issues/a-guest-close-after-a-refused-handshake-can-leave-its-peer-waiting.md @@ -0,0 +1,42 @@ +--- +status: open +kind: defect +opened: 2026-10-09 +--- + +# A guest's close after a refused handshake can leave its peer waiting + +`https_fetch` (`tests/toyos.rs`) runs `https_get` three times on one boot of +`tests/netcase`, each a new process and one TCP connection through +`/system/bin/netstack` to a `rustls` server on the host. The second and third +runs refuse the server's certificate during the handshake, print their +refusal, drop the connection and exit 2. On smoltcp, the stack `netstack` ran +before ToyOS's own replaced it, the server's read of the handshake's next +record was answered by nothing: in one boot its peer's end of stream arrived +and in the other it did not, 180 seconds after the process exited, no FIN and +no reset. netstack logged nothing about either connection. + +The same client and server both on the host (macOS) see each refused +connection end at once with an end of stream and no alert: the client sends +no alert, so the close is all the peer is owed, and it is what was missing. + +Measured at `a944746d5` plus the branch that added `https_fetch`, under QEMU +with slirp, once (logs on that branch's pull request): the +second connection of the boot never ended at the peer; the third did, by the +end of the 180 seconds. Two earlier boots of the same test reported the +second connection silent after 30 seconds. Not reduced, and no capture was +taken of the guest's side, so slirp losing the guest's FIN was not ruled out. + +On ToyOS's own stack, measured at that branch's merge of `main` after #801 +by a probe patch on `https_fetch` that waits up to 150 seconds for each +refused connection's end (patch and logs on that branch's pull request): in +two boots, all four refused connections had already ended at the peer when +the harness looked, after the third run exited. Two boots are no rate, and +`https_fetch` still asserts nothing of these ends. + +**Exit condition**: a guest test whose process closes a connection mid- +handshake, with the peer's flight unread, and exits, sees the host's peer +read the end of the stream within a stated bound, on each of several +connections in one boot. + +**Owner**: whoever holds `issues/toyos-has-its-own-network-stack.md`. diff --git a/issues/a-netcase-boot-under-host-load-said-nothing-past-the-firmwares-screen-clears.md b/issues/a-netcase-boot-under-host-load-said-nothing-past-the-firmwares-screen-clears.md new file mode 100644 index 00000000000..a57c0a02f4b --- /dev/null +++ b/issues/a-netcase-boot-under-host-load-said-nothing-past-the-firmwares-screen-clears.md @@ -0,0 +1,37 @@ +--- +status: open +kind: defect +opened: 2026-10-09 +--- + +# A netcase boot under host load said nothing past the firmware's screen clears + +One whole `cargo test` on the development Mac, at the head of the branch that +added `https_fetch`, went red on `netstack_socket_churn` alone: `[qemu] Boot +timed out waiting for ===READY===; the console carried: nothing at all`, after +487 s. The boot's 16550 file holds 58 bytes, the firmware's +screen-clearing escapes (`ESC[2J ESC[01;01H ESC[=3h`, three times) and +nothing after them: no loader line. The same image booted green in the same +run for `https_fetch` and `libc_sockets`, and `netstack_socket_churn` alone a +minute later was green in 12 s. + +The host's load averages as the suite began were 82.78, 94.69 and 92.31 on +14 cores, other worktrees' suites among them, and 36.29, 47.59 and 67.23 as +it ended; 37.01, 45.47 and 64.77 for the green run alone. The harness had +priced its liveness ceilings at 1.00x. + +A second, at `216f33b35` on the same branch: `cargo test --test toyos-build +-- https_fetch` alone, beside this tree's own `cargo run -- --ci host` and +other worktrees' work, went red the same way after 64 s, its 16550 file the +same 58 bytes. Load averages 89.36, 87.83 and 82.56 as it began and 98.94, +91.36 and 84.48 as it ended. + +Not reduced: whether the firmware is stuck or starved, which a QMP read of a +silent guest's registers would say (`tests/CLAUDE.md`'s method for a death +during boot), and at what rate. + +**Exit**: a rate over repeated boots of this image beside other guests, each +silent one's registers read, that names where the firmware stopped; and the +fix at that cause. + +**Owner**: the orchestrator. diff --git a/issues/the-build-runs-host-tools-outside-rust-and-qemu.md b/issues/the-build-runs-host-tools-outside-rust-and-qemu.md index d45c5a44edf..e2216e5510a 100644 --- a/issues/the-build-runs-host-tools-outside-rust-and-qemu.md +++ b/issues/the-build-runs-host-tools-outside-rust-and-qemu.md @@ -19,8 +19,8 @@ arrives and is not one. M4 and M5 are stages of `issues/toyos-builds-itself.md`. | `sh` running LLVM's `config.guess`, and the POSIX tools and `cc` it runs | LLVM's CMake, whenever this host builds an LLVM, and the C++ runtime's, in every sysroot build, ask it the host's triple, unconditionally (`get_host_triple` in `rust/src/llvm-project/llvm/cmake/modules/GetHostTriple.cmake`, which runs `sh` by name) | refused: a Rust tool does the shell's part, brush 0.4.0: on the development host (macOS, arm64) `config.guess` printed `/bin/sh`'s triple under it, `arm64-apple-darwin27.0.0`, exit 0 each. The script runs `sed`, `uname`, `mktemp`, `grep`, `rm`, `rmdir` and `cc` there under either shell, and that `cc` is the `cc` rows'. Five of the other six are refused, uutils' doing each: under brush with sed 0.2.0, grep 0.2.0 and coreutils 0.12.0's `mktemp`, `rm` and `rmdir`, and nothing else on `PATH` but the host's `uname` and `cc`, it printed that triple, exit 0. `uname` is admitted: coreutils 0.12.0's answers `-p` with `unknown` where macOS's answers `arm`, and `config.guess` reads that as PowerPC, `powerpc-apple-darwin27.0.0`, exit 0 | CMake finds brush as its `sh`, uutils' `sed`, `grep`, `mktemp`, `rm` and `rmdir`, and a Rust `uname` that answers `-p` as the host's does; or M5 runs it in the guest | | `git` for worktrees, submodules, checkouts, fixtures and rustc's bootstrap | adds worktrees (`src/sysroot.rs`); updates submodules (`src/lib.rs`, `src/sysroot.rs`); fetches the fork from the primary's and checks it out (`src/sysroot.rs`); makes the tests' fixture repositories; runs inside rustc's bootstrap | admitted: no Rust tool does the job, gitoxide 0.85 adds, removes and prunes no worktree, updates no submodule, stages, resets and pushes nothing, checks out only a fresh clone and fetches a local path by spawning `git`; a fixture must be what `git` makes, and bootstrap runs `git` itself | M4 runs it in the guest | | `git` for reads, a config write, a commit's paths written out, and clones and fetches over HTTPS | `rev-parse`, `merge-base`, `ls-tree`, `ls-files`, `cat-file`, `config --get-regexp`, `config --blob`, `status`, `diff`, `ls-remote` and `grep`, in the build system and its tests; `config --global --add safe.directory` in CI's containers; `checkout -- ` through an index of its own, which writes the C++ runtime's sources out of the LLVM commit into the stored LLVM (`src/llvm.rs`); every workflow's checkout | refused: a Rust tool does it, gitoxide 0.85, which reads refs, objects, the index, config, worktrees and status, adds a value to a config file and writes it (gix-config 0.58's `File::section_mut_or_create_new`, `SectionMut::push`, `File::write_to`), walks history, diffs, and lists, fetches and clones a remote over HTTPS; `grep` is a search of the files its index names; and gitoxide's CLI 0.59 (gix 0.88) wrote the runtimes' sources of LLVM `849da7d6` into an empty directory, each path's tree through `gix rev parse`, `gix index from-tree` and `gix free index checkout-exclusive`, exit 0 each: the 18759 files `git` writes there, byte for byte and mode for mode | those are gitoxide's | -| `cc`, `c++` and `ar` on a Linux host, `build-essential` in CI's containers | rustc links every host binary through `cc`; `cc` and `c++` compile LLVM, clang, LLD and `rustc_llvm` (`src/llvm.rs` names both to bootstrap); `cc` compiles ring's C and assembly in every build of the build system itself, whose HTTP agent is rustls on ring (`src/release.rs`); `ar` archives what `cc::Build` compiles, ring's included | admitted: no Rust tool compiles C or C++, or takes rustc's host link; ring is the one TLS provider the tree takes (owner, 2026-10-02), over a provider in Rust alone | M5: no host in the loop | -| the toolchain's own `clang`, `llvm-ar`, `rust-lld` and `llvm-config`, built from `ToyOSOrg/llvm-project` | rustc links every guest binary with `rust-lld`; `clang` compiles the C corpus (`tests/common/compile.rs`) and, with `llvm-ar`, doomgeneric through `cc::Build` (`src/clang.rs`); rustc's bootstrap asks `llvm-config` how to link LLVM | admitted: our fork's C++, which ToyOS can one day build and run; no Rust tool compiles C, `cc::Build` archives with an `ar`, bootstrap reads LLVM through `llvm-config`, and `CLAUDE.md` links everything with `rust-lld` | M5: no host in the loop | +| `cc`, `c++` and `ar` on a Linux host, `build-essential` in CI's containers | rustc links every host binary through `cc`; `cc` and `c++` compile LLVM, clang, LLD and `rustc_llvm` (`src/llvm.rs` names both to bootstrap); `cc` compiles ring's C and assembly in every build of the build system itself, whose HTTP agent is rustls on ring (`src/release.rs`), and in every build of doom's build script, whose download of doomgeneric is `ureq` on rustls on ring (`userland/doom/build.rs`); `ar` archives what `cc::Build` compiles, ring's included | admitted: no Rust tool compiles C or C++, or takes rustc's host link; ring is the one TLS provider the tree takes (owner, 2026-10-02), over a provider in Rust alone | M5: no host in the loop | +| the toolchain's own `clang`, `llvm-ar`, `rust-lld` and `llvm-config`, built from `ToyOSOrg/llvm-project` | rustc links every guest binary with `rust-lld`; `clang` compiles the C corpus (`tests/common/compile.rs`) and, with `llvm-ar`, through `cc::Build` the C of every guest crate that has any (`src/clang.rs`, `src/build.rs`'s `GuestEnv`): doomgeneric, and ring's C and assembly in a test crate's `https_get` and `ring_kat`; rustc's bootstrap asks `llvm-config` how to link LLVM | admitted: our fork's C++, which ToyOS can one day build and run; no Rust tool compiles C, `cc::Build` archives with an `ar`, bootstrap reads LLVM through `llvm-config`, and `CLAUDE.md` links everything with `rust-lld` | M5: no host in the loop | | `ovmf-generic`, `qemu-efi-aarch64` | the x86-64 and AArch64 UEFI firmware of CI's guest containers (`src/firmware.rs`), packaged by Debian apart from QEMU | admitted: QEMU's own firmware, and no Rust firmware does its job | the instrument's QEMU carries its own firmware | | `ca-certificates` | the trust store `git` and `curl` verify against in CI's containers | admitted: data both of them need | goes when neither runs there | | the T14's Ubuntu and every tool `src/metal.rs` runs on it over `ssh` | the metal loop, on the T14 and never on a development host | outside the rule: recovery equipment on a test machine, not the build's host | they leave with Ubuntu (`issues/the-machine-updates-itself-without-ubuntu.md`) | diff --git a/issues/the-internet-clients-work-unchanged.md b/issues/the-internet-clients-work-unchanged.md index 3385af0af72..b0dd28eea41 100644 --- a/issues/the-internet-clients-work-unchanged.md +++ b/issues/the-internet-clients-work-unchanged.md @@ -33,27 +33,21 @@ netd, `toyos::net` and std's ToyOS networking in the `rust/` fork. program on `rustls`; the program and its judge's server installed `rustls-rustcrypto` 0.0.2-alpha, and #660 cut both. The row comes back on `ring` and waits for it; `rustls-rustcrypto` does not come back (owner, - 2026-10-02). Published `ring` 0.17.14 does not link for - `x86_64-unknown-toyos`: its `build.rs` picks its assembly by an OS list - that lacks `toyos`, and `src/rand.rs` has an OS list of its own. With - `"toyos"` added to `build.rs`'s `LINUX_ABI` and `target_os = "toyos"` to - `src/rand.rs` it builds for both ToyOS targets. In an x86-64 QEMU guest - it passes known answers (SHA-256, - SHA-512, HMAC, X25519, ChaCha20-Poly1305, AES-GCM, Ed25519, P-256, - `SystemRandom`), and `ureq` 3.4.2 on `rustls` 0.23.45 fetches 320000 - bytes over TLS 1.3 from a server on the host and refuses a wrong name and - an untrusted root (#682, comment 5968053596). What stands before it - lands: a git fork's - `build.rs` runs `perl`, an arrival - `issues/the-build-runs-host-tools-outside-rust-and-qemu.md` does not - declare, and leaves C asserts on, so `__assert_fail` is undefined unless - ring builds with `debug = false` or `toyos_c` is linked; `src/build.rs` - gives the C compiler's environment (`cc_env`) to userland builds alone, so - a test crate's C is compiled by the host's `cc`; and not run: AArch64, the - T14, a `git =` dependency, the licence gate over ring in an image. - `rustls-rustcrypto` is - still named by doom's build script, which installs it on the host. - Open: what doom's build script installs instead. + 2026-10-02). `ring` is `ToyOSOrg/ring`'s `toyos-0.17.14`: the published + 0.17.14 package byte for byte, its build script telling a packaged tree + from a source tree by `pregenerated/` rather than `.git` (so a `git` + dependency runs no `perl`, and its C asserts and warnings-as-errors stay + off, leaving nothing to call `__assert_fail`), and `toyos` in `LINUX_ABI` + and in `src/rand.rs`'s `getrandom` list. The build writes the Mozilla + roots of `webpki-root-certs` to ROOT as `/system/etc/ssl/cert.pem` + (`build::TRUST_ROOTS`), with the text of their licence, + CDLA-Permissive-2.0, beside them, which the owner allowed for these roots + and the licence gate holds them to; `https_fetch` stages a file of its own + holding them and its harness's authority. In an x86-64 QEMU guest + `ring_kat` passes known answers, RSA and P-384 among them, and `https_fetch` has an unchanged `ureq` 3 on + `rustls` 0.23 fetch a body over TLS 1.3 byte-exact, send the project's + `User-Agent`, and refuse a wrong name and an untrusted root. Not run: + AArch64, the T14. **Exit**: `https_tls13` is a `METAL` row: on the T14's I219 an unmodified `ureq` and `rustls` client on the `ring` provider fetches over TLS 1.3 from a server the harness runs, and refuses a wrong name and an untrusted diff --git a/licenses/CDLA-Permissive-2.0-webpki-root-certs.txt b/licenses/CDLA-Permissive-2.0-webpki-root-certs.txt new file mode 100644 index 00000000000..8e84169cf7b --- /dev/null +++ b/licenses/CDLA-Permissive-2.0-webpki-root-certs.txt @@ -0,0 +1,61 @@ +# Community Data License Agreement - Permissive - Version 2.0 + +This is the Community Data License Agreement - Permissive, Version +2.0 (the "agreement"). Data Provider(s) and Data Recipient(s) agree +as follows: + +## 1. Provision of the Data + +1.1. A Data Recipient may use, modify, and share the Data made +available by Data Provider(s) under this agreement if that Data +Recipient follows the terms of this agreement. + +1.2. This agreement does not impose any restriction on a Data +Recipient's use, modification, or sharing of any portions of the +Data that are in the public domain or that may be used, modified, +or shared under any other legal exception or limitation. + +## 2. Conditions for Sharing Data + +2.1. A Data Recipient may share Data, with or without modifications, so +long as the Data Recipient makes available the text of this agreement +with the shared Data. + +## 3. No Restrictions on Results + +3.1. This agreement does not impose any restriction or obligations +with respect to the use, modification, or sharing of Results. + +## 4. No Warranty; Limitation of Liability + +4.1. All Data Recipients receive the Data subject to the following +terms: + +THE DATA IS PROVIDED ON AN "AS IS" BASIS, WITHOUT REPRESENTATIONS, +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. + +NO DATA PROVIDER SHALL HAVE ANY LIABILITY FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING +WITHOUT LIMITATION LOST PROFITS), 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 DATA OR RESULTS, +EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + +## 5. Definitions + +5.1. "Data" means the material received by a Data Recipient under +this agreement. + +5.2. "Data Provider" means any person who is the source of Data +provided under this agreement and in reliance on a Data Recipient's +agreement to its terms. + +5.3. "Data Recipient" means any person who receives Data directly +or indirectly from a Data Provider and agrees to the terms of this +agreement. + +5.4. "Results" means any outcome obtained by computational analysis +of Data, including for example machine learning models and models' +insights. diff --git a/src/build.rs b/src/build.rs index b383792baab..c96861e52a3 100644 --- a/src/build.rs +++ b/src/build.rs @@ -337,11 +337,16 @@ struct GuestEnv { image_key: String, /// Whose floor the loader keeps (`signing::FLOOR_ENV`), which follows the key. floor_scope: &'static str, + /// `CSysroot::cc_env` for the architecture's userland: every guest crate + /// that compiles C compiles it with the toolchain's clang against libc's C + /// sysroot, never with the host's `cc`. + cc: Vec<(String, String)>, } impl GuestEnv { - fn new(sysroot: Sysroot) -> Self { + fn new(sysroot: Sysroot, arch: Arch) -> Self { Self { + cc: crate::clang::CSysroot::of(sysroot.dir(), arch).cc_env(), sysroot, image_key: crate::signing::key().public_hex(), floor_scope: crate::signing::key().floor_scope().word(), @@ -371,7 +376,8 @@ fn cargo_build( .env_remove("RUSTFLAGS") .env(crate::signing::KEY_ENV, &env.image_key) .env(crate::signing::FLOOR_ENV, env.floor_scope) - .env_remove("RUSTC"); + .env_remove("RUSTC") + .envs(env.cc.iter().map(|(k, v)| (k, v))); for (k, v) in extra_env { cmd.env(k, v); } @@ -710,6 +716,29 @@ fn release(root: &Path, toolchain: &crate::keystore::Key, arch: Arch) -> toyos_o } } +/// Where on ROOT the certificate authorities every TLS client trusts are: +/// `/system/etc/ssl/cert.pem`. +pub const TRUST_ROOTS: &str = "etc/ssl/cert.pem"; + +/// The crate [`trust_roots`] reads them out of: `licence::judge` holds them to +/// its licence and [`TRUST_ROOTS_LICENCE`] to its licence text. +pub const TRUST_ROOTS_CRATE: &str = "webpki-root-certs"; + +/// Where on ROOT their licence's text is, and the text: CDLA-Permissive-2.0 +/// lets the roots be shared only with it. +pub const TRUST_ROOTS_LICENCE: (&str, &[u8]) = ( + "etc/ssl/CDLA-Permissive-2.0.txt", + include_bytes!("../licenses/CDLA-Permissive-2.0-webpki-root-certs.txt"), +); + +/// The Mozilla root program's authorities as `webpki-root-certs` publishes +/// them, each certificate a PEM block in the published order. +pub fn trust_roots() -> Vec { + let config = pem::EncodeConfig::new().set_line_ending(pem::LineEnding::LF); + let roots = webpki_root_certs::TLS_SERVER_ROOT_CERTS.iter(); + roots.map(|cert| pem::encode_config(&pem::Pem::new("CERTIFICATE", cert.to_vec()), config)).collect::().into_bytes() +} + fn build_and_assemble( root: &Path, config: &SystemConfig, @@ -731,6 +760,9 @@ fn build_and_assemble( root_files.extend(assets::collect(&config.assets, &programs)); } + root_files.push((TRUST_ROOTS.to_string(), trust_roots())); + root_files.push((TRUST_ROOTS_LICENCE.0.to_string(), TRUST_ROOTS_LICENCE.1.to_vec())); + // Extra files (test binaries, shared libs) for (name, data) in extra_files { root_files.push((name.clone(), data.clone())); @@ -781,11 +813,6 @@ fn build_programs( let ws_target = root.join(format!("target/{target}/{PROFILE}")); - // Every userland crate that compiles C compiles it with the toolchain's - // clang against libc's C sysroot. - let cc_env = crate::clang::CSysroot::of(env.sysroot.dir(), arch).cc_env(); - let cc_env: Vec<(&str, &str)> = cc_env.iter().map(|(k, v)| (k.as_str(), v.as_str())).collect(); - // Build and read under one hold, exactly as `build_toyos_bins` does and for // the same reason: a program's path is keyed on (crate, target, profile) // alone, so every config in this run writes and reads the same @@ -800,7 +827,7 @@ fn build_programs( extra.push("-p"); extra.push(pkg); } - cargo_build(root, target, &extra, env, &cc_env, quiet); + cargo_build(root, target, &extra, env, &[], quiet); } for c in &programs { @@ -1701,7 +1728,7 @@ fn shipped_parts(root: &Path, boot: &Boot, plan: &Plan) -> (Vec, Vec, Ve // this worktree's crate targets can land inside this build. let mut lock = buildlock::shared(root, "build"); let config = parse_config(&boot.config); - let env = GuestEnv::new(toolchain::ensure(root, &mut lock)); + let env = GuestEnv::new(toolchain::ensure(root, &mut lock), arch); invalidate_stale(&mut lock, &env.sysroot.identity, &[root.to_path_buf()]); @@ -1915,7 +1942,7 @@ pub fn build_test_parts( // back after the userland build, and a clean landing in between is the // same defect as one landing mid-compile. let mut lock = buildlock::shared(root, "test image"); - let env = GuestEnv::new(toolchain::ensure(root, &mut lock)); + let env = GuestEnv::new(toolchain::ensure(root, &mut lock), arch); invalidate_stale(&mut lock, &env.sysroot.identity, &[root.to_path_buf()]); @@ -2062,7 +2089,7 @@ struct TestBuild { impl TestBuild { fn begin(root: &Path, arch: Arch, what: &str, stale_targets: &[PathBuf]) -> Self { let mut lock = buildlock::shared(root, what); - let env = GuestEnv::new(toolchain::ensure(root, &mut lock)); + let env = GuestEnv::new(toolchain::ensure(root, &mut lock), arch); invalidate_stale(&mut lock, &env.sysroot.identity, stale_targets); let artifact = buildlock::artifact(root); TestBuild { target: arch.userland(), env, _lock: lock, _artifact: artifact } diff --git a/src/licence.rs b/src/licence.rs index 6bfe3e1e1fd..33433164732 100644 --- a/src/licence.rs +++ b/src/licence.rs @@ -13,6 +13,12 @@ //! superset of what one image links, and a crate only another platform pulls in //! can red here. Git and path packages are judged exactly as registry ones. //! +//! **The trust roots are judged by the crate the build copies them out of**, +//! [`crate::build::TRUST_ROOTS_CRATE`], which no shipped package reaches: by +//! its `license` as data, the one place [`DATA_ONLY`] passes, and refused +//! unless the licence text the image carries beside them is that crate's own +//! `LICENSE`, byte for byte. +//! //! **A committed file is judged by its [`COMMITTED_FILES`] row**, //! whose [`Terms`] column is its licence. A file ships when it is under a //! shipped asset directory or a shipped path package's directory, or when such @@ -77,6 +83,31 @@ const ALLOWED_WITH: &[(&str, &str)] = &[("Apache-2.0", "LLVM-exception")]; /// Allowed for a [`Terms::Font`] row, and nowhere else. const FONTS_ONLY: &[&str] = &["OFL-1.1"]; +/// Allowed for the data the build writes into an image out of +/// [`crate::build::TRUST_ROOTS_CRATE`], and nowhere else: the owner allowed +/// CDLA-Permissive-2.0 for the Mozilla roots. +const DATA_ONLY: &[&str] = &["CDLA-Permissive-2.0"]; + +/// What a licence is judged as: [`FONTS_ONLY`] passes only for a font and +/// [`DATA_ONLY`] only for the trust roots. +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +enum Scope { + Code, + Font, + Data, +} + +impl Scope { + fn allows(self, id: &str) -> bool { + ALLOWED.contains(&id) + || match self { + Scope::Code => false, + Scope::Font => FONTS_ONLY.contains(&id), + Scope::Data => DATA_ONLY.contains(&id), + } + } +} + /// Surfaced by name in every verdict, not refused. const NAMED: &[&str] = &["MPL-2.0"]; @@ -519,10 +550,16 @@ pub const COMMITTED_FILES: &[(&str, &str, &str, Terms)] = &[ ]; /// `NOTICE` sections that name no files, and why. -const PROSE: &[(&str, &str)] = &[( - "Rust crates and the forks", - "every crate, fork or not, is judged by the crate half of this gate", -)]; +const PROSE: &[(&str, &str)] = &[ + ( + "Rust crates and the forks", + "every crate, fork or not, is judged by the crate half of this gate", + ), + ( + "licenses/CDLA-Permissive-2.0-webpki-root-certs.txt — the Mozilla roots, CDLA-Permissive-2.0", + "the roots the build writes out of a crate are judged, with this text beside them, by `judge_trust_roots`", + ), +]; // --- Expressions ------------------------------------------------------------- @@ -629,11 +666,10 @@ fn parse_with(tokens: &[String], at: &mut usize) -> Result { } /// The leaves that keep `expr` from passing; empty when it passes. -fn refused(expr: &Expr, font: bool) -> Vec { +fn refused(expr: &Expr, scope: Scope) -> Vec { match expr { Expr::Id(id) => { - let ok = ALLOWED.contains(&id.as_str()) || (font && FONTS_ONLY.contains(&id.as_str())); - if ok { + if scope.allows(id) { vec![] } else { vec![id.clone()] @@ -646,9 +682,9 @@ fn refused(expr: &Expr, font: bool) -> Vec { vec![format!("{id} WITH {exception}")] } } - Expr::And(parts) => parts.iter().flat_map(|p| refused(p, font)).collect(), + Expr::And(parts) => parts.iter().flat_map(|p| refused(p, scope)).collect(), Expr::Or(parts) => { - let each: Vec> = parts.iter().map(|p| refused(p, font)).collect(); + let each: Vec> = parts.iter().map(|p| refused(p, scope)).collect(); if each.iter().any(Vec::is_empty) { vec![] } else { @@ -666,17 +702,17 @@ fn names(expr: &Expr) -> bool { Expr::And(parts) => parts.iter().any(names), Expr::Or(parts) => parts .iter() - .filter(|p| refused(p, false).is_empty()) + .filter(|p| refused(p, Scope::Code).is_empty()) .all(names), } } /// Why `licence` does not pass, or `None` when it does. -fn judge_licence(licence: &str, font: bool) -> Option { +fn judge_licence(licence: &str, scope: Scope) -> Option { match parse(licence) { Err(why) => Some(format!("not an SPDX expression: {why}")), Ok(expr) => { - let leaves = refused(&expr, font); + let leaves = refused(&expr, scope); (!leaves.is_empty()).then(|| format!("not allowed: {}", leaves.join(", "))) } } @@ -755,8 +791,8 @@ impl Report { } /// Judge `finding`'s licence, and record it as a refusal or an MPL name. - fn judge(&mut self, finding: Finding, font: bool) { - match judge_licence(&finding.licence, font) { + fn judge(&mut self, finding: Finding, scope: Scope) { + match judge_licence(&finding.licence, scope) { Some(why) => self.find(Finding { why, ..finding }), None if parse(&finding.licence).is_ok_and(|e| names(&e)) => { let (subject, licence, via) = (finding.subject, finding.licence, finding.via); @@ -938,7 +974,7 @@ fn judge_crates(metadata: &Value, roots: &[PathBuf], report: &mut Report) -> Res into: into[dep].clone(), }; match (str_of(package, "license"), str_of(package, "license_file")) { - (Some(licence), _) => report.judge(finding(licence, String::new()), false), + (Some(licence), _) => report.judge(finding(licence, String::new()), Scope::Code), (None, Some(file)) => report.find(finding( "", format!("declares only a licence file, {file}, which no gate reads"), @@ -949,6 +985,47 @@ fn judge_crates(metadata: &Value, roots: &[PathBuf], report: &mut Report) -> Res Ok(local) } +// --- The trust roots --------------------------------------------------------- + +/// Judge the roots out of `package`, whose `LICENSE` is `text`, beside which an +/// image ships `shipped`. +fn judge_trust_roots(package: &Value, text: &[u8], shipped: &[u8], report: &mut Report) { + let name = str_of(package, "name").unwrap_or("?"); + let version = str_of(package, "version").unwrap_or("?"); + let finding = Finding { + subject: format!("crate {name} {version}, the data of {}", crate::build::TRUST_ROOTS), + kind: Kind::Crate, + key: name.to_string(), + licence: str_of(package, "license").unwrap_or("").to_string(), + why: String::new(), + via: vec!["build::trust_roots".to_string()], + into: BTreeSet::from([None]), + }; + if text == shipped { + report.judge(finding, Scope::Data); + } else { + let why = format!("ships {} beside them, which is not its LICENSE", crate::build::TRUST_ROOTS_LICENCE.0); + report.find(Finding { why, ..finding }); + } +} + +/// [`judge_trust_roots`] on the build's own workspace, out of `metadata`, a +/// document of it: the crate is a non-optional dependency of the root +/// package, so every feature set resolves it. +fn judge_trust_roots_of(metadata: &Value, report: &mut Report) -> Result<(), String> { + let name = crate::build::TRUST_ROOTS_CRATE; + let packages = metadata["packages"].as_array().ok_or("cargo metadata printed no packages")?; + let found: Vec<&Value> = packages.iter().filter(|p| str_of(p, "name") == Some(name)).collect(); + let [package] = found[..] else { + return Err(format!("the build's workspace resolves {} packages named {name}, not one", found.len())); + }; + let manifest = str_of(package, "manifest_path").ok_or_else(|| format!("{name} has no manifest path"))?; + let licence = Path::new(manifest).with_file_name("LICENSE"); + let text = std::fs::read(&licence).map_err(|e| format!("read {}: {e}", licence.display()))?; + judge_trust_roots(package, &text, crate::build::TRUST_ROOTS_LICENCE.1, report); + Ok(()) +} + // --- Committed files --------------------------------------------------------- /// Where committed files ship from, as root-relative paths. @@ -1004,8 +1081,11 @@ fn judge_files(ledger: &[Row], tracked: &[String], shipping: &Shipping, report: } for (path, _, _, terms) in ledger { if shipping.ships(path) { - let font = matches!(terms, Terms::Font(_)); - report.judge(Finding::at(Kind::File, path, terms.expr(), String::new()), font); + let scope = match terms { + Terms::Spdx(_) => Scope::Code, + Terms::Font(_) => Scope::Font, + }; + report.judge(Finding::at(Kind::File, path, terms.expr(), String::new()), scope); } } } @@ -1101,7 +1181,7 @@ fn judge_notice( report.notes.push(format!("NOTICE section {path} ({expr}) is not shipped")); continue; } - report.judge(Finding::at(Kind::Notice, path, expr, String::new()), false); + report.judge(Finding::at(Kind::Notice, path, expr, String::new()), Scope::Code); } } @@ -1284,6 +1364,13 @@ pub fn judge(root: &Path) -> Result { roots: vec![library.join("std")], }); + let manifest = root.join("Cargo.toml"); + let build = docs + .iter() + .find(|d| d.members.contains(&manifest)) + .ok_or_else(|| format!("no shipped crate is in the workspace of {}", manifest.display()))?; + judge_trust_roots_of(&build.metadata, &mut report)?; + let mut local = Local::default(); for doc in &docs { let found = judge_crates(&doc.metadata, &doc.roots, &mut report)?; @@ -1329,7 +1416,7 @@ mod tests { use serde_json::json; fn verdict_of(licence: &str) -> Option { - judge_licence(licence, false) + judge_licence(licence, Scope::Code) } #[test] @@ -1343,7 +1430,11 @@ mod tests { "an unrecognised licence passed" ); assert!(verdict_of("OFL-1.1").is_some(), "OFL passed outside a font"); - assert_eq!(judge_licence("OFL-1.1", true), None); + assert_eq!(judge_licence("OFL-1.1", Scope::Font), None); + assert!(judge_licence("OFL-1.1", Scope::Data).is_some(), "OFL passed as data"); + assert!(verdict_of("CDLA-Permissive-2.0").is_some(), "CDLA passed outside data"); + assert!(judge_licence("CDLA-Permissive-2.0", Scope::Font).is_some(), "CDLA passed for a font"); + assert_eq!(judge_licence("CDLA-Permissive-2.0", Scope::Data), None); assert_eq!(verdict_of("MPL-2.0"), None); } @@ -1694,6 +1785,23 @@ ub_checks"#; assert!(crates(&d).findings.is_empty()); } + /// **The roots pass under their crate's licence as data, and only with + /// that crate's own licence text beside them.** + #[test] + fn the_trust_roots_pass_only_with_their_crates_licence_text() { + let judged = |licence: &str, shipped: &[u8]| { + let package = json!({"name": "roots", "version": "1.0.0", "license": licence}); + let mut report = Report::default(); + judge_trust_roots(&package, b"the text", shipped, &mut report); + report.findings + }; + assert_eq!(judged("CDLA-Permissive-2.0", b"the text"), []); + let other = judged("CDLA-Permissive-2.0", b"another text"); + assert!(other.len() == 1 && other[0].why.contains("not its LICENSE"), "{other:?}"); + let sharing = judged("CDLA-Sharing-1.0", b"the text"); + assert!(sharing.len() == 1 && sharing[0].why.contains("CDLA-Sharing-1.0"), "{sharing:?}"); + } + #[test] fn a_root_the_metadata_does_not_hold_is_refused() { let d = doc(&[("other", Some("MIT"), None, None)], &[]); diff --git a/tests/common/https.rs b/tests/common/https.rs new file mode 100644 index 00000000000..234fa7f7ac9 --- /dev/null +++ b/tests/common/https.rs @@ -0,0 +1,164 @@ +//! The HTTPS server a guest's client is judged against: `rustls` on `ring` on +//! the host, listening on loopback and reached from the guest through slirp at +//! [`HOST`], presenting a certificate an [`Authority`] made for this run. +//! +//! It is the client's peer, not its oracle: both ends are `rustls` on `ring`. +//! What it reports is what only the peer sees — that a connection arrived, +//! the protocol version the client agreed to and the `User-Agent` it sent. + +use std::io::{BufRead, BufReader, Write}; +use std::net::{IpAddr, TcpListener, TcpStream}; +use std::sync::mpsc::{self, Receiver, RecvTimeoutError}; +use std::sync::Arc; +use std::thread; +use std::time::Duration; + +use rcgen::{BasicConstraints, CertificateParams, CertifiedIssuer, DnType, IsCa, KeyPair, KeyUsagePurpose, SanType}; +use rustls::pki_types::{CertificateDer, PrivateKeyDer, PrivatePkcs8KeyDer}; + +/// The host as slirp shows it to the guest; it carries a connection to this +/// address to the host's loopback. +pub const HOST: &str = "10.0.2.2"; + +/// The bytes the server answers every request with: several hundred KiB of a +/// fixed xorshift stream, so a dropped, duplicated or reordered segment +/// changes the hash, and the same every run. +pub fn body() -> Vec { + const BYTES: usize = 384 * 1024; + let mut state: u64 = 0x9e37_79b9_7f4a_7c15; + let mut body = Vec::with_capacity(BYTES); + while body.len() < BYTES { + state ^= state << 13; + state ^= state >> 7; + state ^= state << 17; + body.extend_from_slice(&state.to_le_bytes()); + } + body.truncate(BYTES); + body +} + +/// A certificate authority made for this run alone, whose key never leaves +/// this process. +pub struct Authority(CertifiedIssuer<'static, KeyPair>); + +impl Authority { + pub fn new(name: &str) -> Self { + let mut params = CertificateParams::default(); + params.distinguished_name.push(DnType::CommonName, name); + params.is_ca = IsCa::Ca(BasicConstraints::Unconstrained); + params.key_usages = vec![KeyUsagePurpose::KeyCertSign, KeyUsagePurpose::DigitalSignature]; + let key = KeyPair::generate().expect("an authority's key"); + Self(CertifiedIssuer::self_signed(params, key).expect("an authority's certificate")) + } + + /// Its certificate, as one PEM block. + pub fn pem(&self) -> String { + self.0.pem() + } + + /// A certificate for the one address `san` and its key, signed by this + /// authority. + pub fn leaf(&self, san: IpAddr) -> (CertificateDer<'static>, PrivateKeyDer<'static>) { + let mut params = CertificateParams::default(); + params.subject_alt_names = vec![SanType::IpAddress(san)]; + let key = KeyPair::generate().expect("a server's key"); + let cert = params.signed_by(&key, &self.0).expect("a server's certificate"); + (cert.der().clone(), PrivatePkcs8KeyDer::from(key.serialize_der()).into()) + } +} + +/// What the server saw of a connection: [`Seen::Accepted`] as it arrives, +/// then one of the other two as it ends. +#[derive(Debug)] +pub enum Seen { + Accepted, + /// A handshake that completed and a request answered with the whole body. + Served { version: Option, path: String, user_agents: Vec }, + /// A handshake the server could not complete. + Failed, +} + +/// A server on a loopback port of its own, answering each connection in turn +/// with [`body`]-shaped bytes, until this process ends. +pub struct Server { + pub port: u16, + seen: Receiver, +} + +impl Server { + pub fn start(cert: (CertificateDer<'static>, PrivateKeyDer<'static>), body: Arc>) -> Result { + let (chain, key) = cert; + let config = rustls::ServerConfig::builder_with_provider(Arc::new(rustls::crypto::ring::default_provider())) + .with_safe_default_protocol_versions() + .and_then(|b| b.with_no_client_auth().with_single_cert(vec![chain], key)) + .map_err(|e| format!("the HTTPS server's configuration: {e}"))?; + let config = Arc::new(config); + let listener = TcpListener::bind(("127.0.0.1", 0)).map_err(|e| format!("the HTTPS server: {e}"))?; + let port = listener.local_addr().map_err(|e| format!("the HTTPS server's port: {e}"))?.port(); + let (tx, seen) = mpsc::channel(); + // Ends with the process, or once nobody reads what it saw. + thread::spawn(move || { + for tcp in listener.incoming() { + let tcp = tcp.expect("accept a connection on the HTTPS server's port"); + if tx.send(Seen::Accepted).is_err() || tx.send(serve(&config, tcp, &body)).is_err() { + return; + } + } + }); + Ok(Self { port, seen }) + } + + /// The next thing seen, waited for up to `within`. + pub fn next(&self, within: Duration) -> Result { + self.seen.recv_timeout(within).map_err(|e| match e { + RecvTimeoutError::Timeout => format!("the server on port {} saw nothing more within {within:?}", self.port), + RecvTimeoutError::Disconnected => format!("the HTTPS server on port {} is gone", self.port), + }) + } + + /// What it has seen past what was read, without waiting. + pub fn more(&self) -> Vec { + self.seen.try_iter().collect() + } +} + +/// One connection: the handshake, one request read to its blank line, and +/// the body after a header that gives its length. +fn serve(config: &Arc, mut tcp: TcpStream, body: &[u8]) -> Seen { + let mut conn = rustls::ServerConnection::new(config.clone()).expect("a server connection"); + while conn.is_handshaking() { + if conn.complete_io(&mut tcp).is_err() { + return Seen::Failed; + } + } + let version = conn.protocol_version(); + let mut stream = rustls::Stream::new(&mut conn, &mut tcp); + let mut request = BufReader::new(&mut stream); + let mut first = String::new(); + request.read_line(&mut first).expect("the request line"); + let path = first.split_whitespace().nth(1).unwrap_or_default().to_string(); + let mut user_agents = Vec::new(); + loop { + let mut line = String::new(); + request.read_line(&mut line).expect("a request header"); + let line = line.trim_end(); + if line.is_empty() { + break; + } + if let Some((name, value)) = line.split_once(':') { + if name.eq_ignore_ascii_case("user-agent") { + user_agents.push(value.trim().to_string()); + } + } + } + drop(request); + let head = format!("HTTP/1.1 200 OK\r\nContent-Length: {}\r\nConnection: close\r\n\r\n", body.len()); + stream.write_all(head.as_bytes()).expect("the response's head"); + stream.write_all(body).expect("the response's body"); + stream.flush().expect("the response"); + conn.send_close_notify(); + while conn.wants_write() { + conn.write_tls(&mut tcp).expect("the close_notify"); + } + Seen::Served { version, path, user_agents } +} diff --git a/tests/common/mod.rs b/tests/common/mod.rs index 291983fd0b6..1a4e52d7ea3 100644 --- a/tests/common/mod.rs +++ b/tests/common/mod.rs @@ -7,6 +7,8 @@ pub mod compile; /// The device boot: what `tests/metaldevicecase` measures. pub mod devices; pub mod faults; +/// The HTTPS server a guest's client fetches from. +pub mod https; pub mod iommu; pub mod irqcensus; /// The `isa` claim's rows on the T14. diff --git a/tests/toyos-rust-tests/Cargo.lock b/tests/toyos-rust-tests/Cargo.lock index d7afd1b6254..84089081454 100644 --- a/tests/toyos-rust-tests/Cargo.lock +++ b/tests/toyos-rust-tests/Cargo.lock @@ -34,6 +34,12 @@ version = "1.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f2032f911046de80f0a198e0901378627c33f59ea0ac00e363d481118bd70a53" +[[package]] +name = "base64" +version = "0.23.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac07cdecf99051d9a5238b80f35af32cdeba5b336e55d957b318b50137e18da5" + [[package]] name = "bitflags" version = "2.13.1" @@ -61,6 +67,16 @@ version = "1.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fc652a48c352aef3ea3aed32080501cf3ef6ed5da78602a020c991775b0aff04" +[[package]] +name = "cc" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f74872d07caf508b30a21f6836e7d7016a2eaf7d9ff4f48deaa58cd8a0407630" +dependencies = [ + "find-msvc-tools", + "shlex", +] + [[package]] name = "cfg-if" version = "1.0.4" @@ -144,6 +160,12 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" +[[package]] +name = "find-msvc-tools" +version = "0.1.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aedcfb3409746eddb02b9e19ebda1c3394f759a152e48ee875a0844d1b955484" + [[package]] name = "futures-core" version = "0.3.33" @@ -168,12 +190,39 @@ dependencies = [ "slab", ] +[[package]] +name = "getrandom" +version = "0.2.17" +source = "git+https://github.com/ToyOSOrg/getrandom?branch=toyos-0.2-sdk-0.12#2092d1ccd2833f00559ed27850c576ab37fe9347" +dependencies = [ + "cfg-if", + "libc", + "toyos-abi", + "wasi", +] + [[package]] name = "hashbrown" version = "0.17.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ed5909b6e89a2db4456e54cd5f673791d7eca6732202bbf2a9cc504fe2f9b84a" +[[package]] +name = "http" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "918d3568bebf352712bc2ef3d46a8bcf1a75b373be6539de198e9105cbbf9ce0" +dependencies = [ + "bytes", + "itoa", +] + +[[package]] +name = "httparse" +version = "1.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6dbf3de79e51f3d586ab4cb9d5c3e2c14aa28ed23d180cf89b4df0454a69cc87" + [[package]] name = "indexmap" version = "2.14.0" @@ -193,6 +242,12 @@ dependencies = [ "toyos-inspect", ] +[[package]] +name = "itoa" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682" + [[package]] name = "jni" version = "0.22.4" @@ -479,6 +534,12 @@ version = "1.21.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50" +[[package]] +name = "percent-encoding" +version = "2.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220" + [[package]] name = "pin-project-lite" version = "0.2.17" @@ -518,6 +579,19 @@ dependencies = [ "proc-macro2", ] +[[package]] +name = "ring" +version = "0.17.14" +source = "git+https://github.com/ToyOSOrg/ring?branch=toyos-0.17.14#34818c4599e7d6233cf42e80de5cd309190e523c" +dependencies = [ + "cc", + "cfg-if", + "getrandom", + "libc", + "untrusted", + "windows-sys 0.52.0", +] + [[package]] name = "rustc_version" version = "0.4.1" @@ -527,6 +601,41 @@ dependencies = [ "semver", ] +[[package]] +name = "rustls" +version = "0.23.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d41d731c7d2f962d1ccc364cec258de3c0e93b38c2fb3ba97ac74513048d634" +dependencies = [ + "log", + "once_cell", + "ring", + "rustls-pki-types", + "rustls-webpki", + "subtle", + "zeroize", +] + +[[package]] +name = "rustls-pki-types" +version = "1.15.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2f4925028c7eb5d1fcdaf196971378ed9d2c1c4efc7dc5d011256f76c99c0a96" +dependencies = [ + "zeroize", +] + +[[package]] +name = "rustls-webpki" +version = "0.103.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3c3cf1d8b1e7d4927e2d154c3fcb02979afb9939629c62cd9048d4f07b60ac2" +dependencies = [ + "ring", + "rustls-pki-types", + "untrusted", +] + [[package]] name = "rustversion" version = "1.0.23" @@ -568,6 +677,12 @@ dependencies = [ "syn 3.0.2", ] +[[package]] +name = "shlex" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8fadd59c855ef2080decdef8ff161eb6661b86933c9d82e5ba29dc602a55aba" + [[package]] name = "simd_cesu8" version = "1.2.0" @@ -590,6 +705,12 @@ version = "0.4.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0c790de23124f9ab44544d7ac05d60440adc586479ce501c1d6d7da3cd8c9cf5" +[[package]] +name = "subtle" +version = "2.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" + [[package]] name = "syn" version = "2.0.119" @@ -725,6 +846,8 @@ dependencies = [ "libloading", "logkeeper-api", "memmap2", + "ring", + "rustls", "toyos", "toyos-abi", "toyos-inspect", @@ -732,6 +855,7 @@ dependencies = [ "toyos-tco", "toyos-trace", "toyos-window", + "ureq", ] [[package]] @@ -761,6 +885,46 @@ version = "1.0.24" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75" +[[package]] +name = "untrusted" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" + +[[package]] +name = "ureq" +version = "3.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a7ac20be9b7726e0bbdbf974c059676d9acb1cd414961f570a4e8231cacd7fc" +dependencies = [ + "base64", + "log", + "percent-encoding", + "rustls", + "rustls-pki-types", + "ureq-proto", + "utf8-zero", + "webpki-roots", +] + +[[package]] +name = "ureq-proto" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f86fd172ccca569e458f61b6bdd6220965a9ef36e672a6852953b51a0e1583be" +dependencies = [ + "base64", + "http", + "httparse", + "log", +] + +[[package]] +name = "utf8-zero" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8c0a043c9540bae7c578c88f91dda8bd82e59ae27c21baca69c8b191aaf5a6e" + [[package]] name = "walkdir" version = "2.5.0" @@ -771,6 +935,12 @@ dependencies = [ "winapi-util", ] +[[package]] +name = "wasi" +version = "0.11.1+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" + [[package]] name = "wasm-bindgen" version = "0.2.126" @@ -826,13 +996,22 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "webpki-roots" +version = "1.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7dcd9d09a39985f5344844e66b0c530a33843579125f23e21e9f0f220850f22a" +dependencies = [ + "rustls-pki-types", +] + [[package]] name = "winapi-util" version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" dependencies = [ - "windows-sys", + "windows-sys 0.61.2", ] [[package]] @@ -936,6 +1115,15 @@ dependencies = [ "windows-link", ] +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets", +] + [[package]] name = "windows-sys" version = "0.61.2" @@ -945,6 +1133,22 @@ dependencies = [ "windows-link", ] +[[package]] +name = "windows-targets" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_gnullvm", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + [[package]] name = "windows-threading" version = "0.2.1" @@ -954,6 +1158,54 @@ dependencies = [ "windows-link", ] +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" + [[package]] name = "winnow" version = "1.0.4" @@ -962,3 +1214,9 @@ checksum = "23b97319f7b8343df12cc98938e5c3eb436064524c8d2b4e30a1d3a36eecdf81" dependencies = [ "memchr", ] + +[[package]] +name = "zeroize" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e13c156562582aa81c60cb29407084cdb54c4164760106ab78e6c5b0858cf64e" diff --git a/tests/toyos-rust-tests/Cargo.toml b/tests/toyos-rust-tests/Cargo.toml index c4e1f7285d4..32329f52a83 100644 --- a/tests/toyos-rust-tests/Cargo.toml +++ b/tests/toyos-rust-tests/Cargo.toml @@ -24,6 +24,11 @@ inspect = { path = "../../userland/inspect" } libloading = { git = "https://github.com/ToyOSOrg/rust_libloading", branch = "toyos-sdk-0.12" } cpal = { git = "https://github.com/ToyOSOrg/cpal", branch = "toyos-0.18.0" } memmap2 = "0.9" +# An HTTPS client as any Rust program writes one, for `https_get`, and the +# primitives under it, for `ring_kat`'s known answers. +ureq = { version = "3", default-features = false, features = ["rustls"] } +rustls = { version = "0.23", default-features = false } +ring = "0.17" # Outside the workspace, so it needs its own redirects: the forks # above depend on ToyOS crates by version, and only a patch maps those to the @@ -32,6 +37,8 @@ memmap2 = "0.9" toyos-abi = { path = "../../toyos-abi" } toyos = { path = "../../toyos" } memmap2 = { git = "https://github.com/ToyOSOrg/memmap2-rs", branch = "toyos-0.9.11" } +ring = { git = "https://github.com/ToyOSOrg/ring", branch = "toyos-0.17.14" } +getrandom = { git = "https://github.com/ToyOSOrg/getrandom", branch = "toyos-0.2-sdk-0.12", package = "getrandom" } [lints.rust] warnings = "deny" diff --git a/tests/toyos-rust-tests/src/bin/https_get.rs b/tests/toyos-rust-tests/src/bin/https_get.rs new file mode 100644 index 00000000000..e72b78dde80 --- /dev/null +++ b/tests/toyos-rust-tests/src/bin/https_get.rs @@ -0,0 +1,77 @@ +//! An HTTPS client as any Rust program writes one: `ureq` on `rustls` on +//! `ring`, as published, trusting the authorities of one roots file and no +//! others, and naming itself with the project's `User-Agent`. +//! +//! argv: the URL, then the roots file. It says one line and ends: +//! `https_get: ok bytes= sha256=` and 0 for a body read to its end, +//! hashed by `ring` over every byte; `https_get: refused ` and 2 for a +//! certificate the client refused; anything else panics. + +use std::io::Read; +use std::process::ExitCode; + +use ureq::tls::{PemItem, RootCerts, TlsConfig}; + +/// Root `CLAUDE.md`'s: the only `User-Agent` ToyOS sends. +const USER_AGENT: &str = "toyos-build (https://github.com/ToyOSOrg/ToyOS)"; + +fn main() -> ExitCode { + let args: Vec = std::env::args().skip(1).collect(); + let [url, roots] = args.as_slice() else { + panic!("usage: https_get , not {args:?}"); + }; + let pem = std::fs::read(roots).unwrap_or_else(|e| panic!("read {roots}: {e}")); + let certs: Vec<_> = ureq::tls::parse_pem(&pem) + .map(|item| match item.unwrap_or_else(|e| panic!("{roots}: {e}")) { + PemItem::Certificate(cert) => cert, + _ => panic!("{roots} holds a PEM block that is not a certificate"), + }) + .collect(); + let tls = TlsConfig::builder().root_certs(RootCerts::new_with_certs(&certs)).build(); + let agent = ureq::Agent::config_builder().user_agent(USER_AGENT).tls_config(tls).build().new_agent(); + + let response = match agent.get(url).call() { + Ok(response) => response, + Err(e) => { + let Some(why) = refused(&e) else { panic!("GET {url}: {e}") }; + let why = match why { + rustls::CertificateError::NotValidForName | rustls::CertificateError::NotValidForNameContext { .. } => { + "not-valid-for-name".to_string() + } + rustls::CertificateError::UnknownIssuer => "unknown-issuer".to_string(), + other => format!("{other:?}"), + }; + println!("https_get: refused {why}"); + return ExitCode::from(2); + } + }; + let mut body = response.into_body().into_reader(); + let mut digest = ring::digest::Context::new(&ring::digest::SHA256); + let mut chunk = vec![0u8; 16 * 1024]; + let mut bytes = 0usize; + loop { + let n = body.read(&mut chunk).unwrap_or_else(|e| panic!("the body of {url}: {e}")); + if n == 0 { + break; + } + digest.update(&chunk[..n]); + bytes += n; + } + let hex: String = digest.finish().as_ref().iter().map(|b| format!("{b:02x}")).collect(); + println!("https_get: ok bytes={bytes} sha256={hex}"); + ExitCode::SUCCESS +} + +/// The certificate `rustls` refused, where that is what `e` is: ureq hands a +/// handshake's error back inside the I/O error of the read that met it. +fn refused(e: &ureq::Error) -> Option<&rustls::CertificateError> { + let tls = match e { + ureq::Error::Rustls(tls) => Some(tls), + ureq::Error::Io(io) => io.get_ref().and_then(|inner| inner.downcast_ref::()), + _ => None, + }; + match tls { + Some(rustls::Error::InvalidCertificate(why)) => Some(why), + _ => None, + } +} diff --git a/tests/toyos-rust-tests/src/bin/ring_kat.rs b/tests/toyos-rust-tests/src/bin/ring_kat.rs new file mode 100644 index 00000000000..03cd43f430d --- /dev/null +++ b/tests/toyos-rust-tests/src/bin/ring_kat.rs @@ -0,0 +1,251 @@ +//! `ring`'s primitives against the known answers their specifications +//! publish: the oracle under every TLS connection a ToyOS program makes, whose +//! own handshake only tests `ring` against `ring` on the host. Each answer is +//! the cited document's, not one this program or `ring` computed. + +#![allow(deprecated, reason = "`ring::test::rand` is the one way to give an X25519 key known bytes")] + +use ring::{aead, agreement, digest, hmac, rand, signature}; + +fn hex(s: &str) -> Vec { + assert!(s.len() % 2 == 0, "odd hex {s:?}"); + (0..s.len()).step_by(2).map(|i| u8::from_str_radix(&s[i..i + 2], 16).expect("hex")).collect() +} + +fn check(what: &str, got: &[u8], want: &str) { + assert_eq!(got, hex(want).as_slice(), "{what}"); + println!("ring_kat: {what} ok"); +} + +fn digests() { + // FIPS 180-2, appendix B.1, D.1 and C.1, and B.3. + check("sha256(abc)", digest::digest(&digest::SHA256, b"abc").as_ref(), + "ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad"); + check("sha384(abc)", digest::digest(&digest::SHA384, b"abc").as_ref(), + "cb00753f45a35e8bb5a03d699ac65007272c32ab0eded1631a8b605a43ff5bed\ + 8086072ba1e7cc2358baeca134c825a7"); + check("sha512(abc)", digest::digest(&digest::SHA512, b"abc").as_ref(), + "ddaf35a193617abacc417349ae20413112e6fa4e89a97ea20a9eeee64b55d39a\ + 2192992a274fc1a836ba3c23a3feebbd454d4423643ce80e2a9ac94fa54ca49f"); + check("sha256(a * 1000000)", digest::digest(&digest::SHA256, &vec![b'a'; 1_000_000]).as_ref(), + "cdc76e5c9914fb9281a1c7e284d73e67f1809a48a497200e046d39ccc7112cd0"); +} + +fn hmac_sha256() { + // RFC 4231, test case 2. + let key = hmac::Key::new(hmac::HMAC_SHA256, b"Jefe"); + check("hmac-sha256 rfc4231 case 2", hmac::sign(&key, b"what do ya want for nothing?").as_ref(), + "5bdcc146bf60754e6a042426089575c75a003f089d2739839dec58b964ec3843"); +} + +fn x25519() { + // RFC 7748, section 6.1. + let alice = hex("77076d0a7318a57d3c16c17251b26645df4c2f87ebc0992ab177fba51db92c2a"); + let bob_public = hex("de9edb7d7b7dc1b4d35b61c2ece435373f8343c85b78674dadfc7e146f882b4f"); + let rng = ring::test::rand::FixedSliceRandom { bytes: &alice }; + let private = agreement::EphemeralPrivateKey::generate(&agreement::X25519, &rng).expect("a key of alice's bytes"); + check("x25519 rfc7748 alice's public key", private.compute_public_key().expect("alice's public key").as_ref(), + "8520f0098930a754748b7ddcb43ef75a0dbf3a0d26381af4eba4a98eaa9b4e6a"); + let peer = agreement::UnparsedPublicKey::new(&agreement::X25519, bob_public); + let shared = agreement::agree_ephemeral(private, &peer, |k| k.to_vec()).expect("the agreement"); + check("x25519 rfc7748 shared secret", &shared, + "4a5d9d5ba4ce2de1728e3bf480350f25e07e21c947d19e3376f09b3c1e161742"); +} + +/// Seal `plaintext` and check the ciphertext and tag, then open it, and +/// refuse it with one bit of the tag flipped. +fn aead_case(what: &str, alg: &'static aead::Algorithm, key: &str, nonce: &str, aad: &str, plaintext: &[u8], ciphertext: &str, tag: &str) { + let key = aead::LessSafeKey::new(aead::UnboundKey::new(alg, &hex(key)).expect("the key")); + let nonce = || aead::Nonce::try_assume_unique_for_key(&hex(nonce)).expect("the nonce"); + let aad = hex(aad); + let mut sealed = plaintext.to_vec(); + key.seal_in_place_append_tag(nonce(), aead::Aad::from(&aad), &mut sealed).expect("seal"); + let (body, got_tag) = sealed.split_at(plaintext.len()); + check(&format!("{what} ciphertext"), body, ciphertext); + check(&format!("{what} tag"), got_tag, tag); + let mut opened = sealed.clone(); + let plain = key.open_in_place(nonce(), aead::Aad::from(&aad), &mut opened).expect("open"); + assert_eq!(plain, plaintext, "{what} opens to its plaintext"); + let mut forged = sealed; + *forged.last_mut().expect("a tag") ^= 1; + assert!(key.open_in_place(nonce(), aead::Aad::from(&aad), &mut forged).is_err(), "{what} opens a forged tag"); + println!("ring_kat: {what} opened, and refused with a flipped tag bit"); +} + +fn aeads() { + // RFC 8439, section 2.8.2. + aead_case( + "chacha20-poly1305 rfc8439 2.8.2", + &aead::CHACHA20_POLY1305, + "808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9f", + "070000004041424344454647", + "50515253c0c1c2c3c4c5c6c7", + b"Ladies and Gentlemen of the class of '99: If I could offer you only one tip for the future, sunscreen would be it.", + "d31a8d34648e60db7b86afbc53ef7ec2a4aded51296e08fea9e2b5a736ee62d6\ + 3dbea45e8ca9671282fafb69da92728b1a71de0a9e060b2905d6a5b67ecd3b36\ + 92ddbd7f2d778b8c9803aee328091b58fab324e4fad675945585808b4831d7bc\ + 3ff4def08e4b7a9de576d26586cec64b6116", + "1ae10b594f09e26a7e902ecbd0600691", + ); + // McGrew and Viega, "The Galois/Counter Mode of Operation", test cases 4 + // and 16. + let plaintext = hex( + "d9313225f88406e5a55909c5aff5269a86a7a9531534f7da2e4c303d8a318a72\ + 1c3c0c95956809532fcf0e2449a6b525b16aedf5aa0de657ba637b39", + ); + aead_case( + "aes-128-gcm case 4", + &aead::AES_128_GCM, + "feffe9928665731c6d6a8f9467308308", + "cafebabefacedbaddecaf888", + "feedfacedeadbeeffeedfacedeadbeefabaddad2", + &plaintext, + "42831ec2217774244b7221b784d0d49ce3aa212f2c02a4e035c17e2329aca12e\ + 21d514b25466931c7d8f6a5aac84aa051ba30b396a0aac973d58e091", + "5bc94fbc3221a5db94fae95ae7121a47", + ); + aead_case( + "aes-256-gcm case 16", + &aead::AES_256_GCM, + "feffe9928665731c6d6a8f9467308308feffe9928665731c6d6a8f9467308308", + "cafebabefacedbaddecaf888", + "feedfacedeadbeeffeedfacedeadbeefabaddad2", + &plaintext, + "522dc1f099567d07f47f37a32a84427d643a8cdcbfe5c0c97598a2bd2555d1aa\ + 8cb08e48590dbb3da7b08b1056828838c5f61e6393ba7a0abcc9f662", + "76fc6ece0f4e1768cddf8853bb2d551b", + ); +} + +fn signatures() { + // RFC 8032, section 7.1, test 1: the key pair from its seed, the + // signature of the empty message, and a refusal of another message. + let pair = signature::Ed25519KeyPair::from_seed_unchecked(&hex( + "9d61b19deffd5a60ba844af492ec2cc44449c5697b326919703bac031cae7f60", + )) + .expect("the seed"); + use signature::KeyPair; + check("ed25519 rfc8032 test 1 public key", pair.public_key().as_ref(), + "d75a980182b10ab7d54bfed3c964073a0ee172f3daa62325af021a68f707511a"); + let sig = pair.sign(b""); + check("ed25519 rfc8032 test 1 signature", sig.as_ref(), + "e5564300c360ac729086e2cc806e828a84877f1eb8e5d974d873e065224901555fb8821590a33bacc61e39701cf9b46bd25bf5f0595bbe24655141438e7a100b"); + let public = signature::UnparsedPublicKey::new(&signature::ED25519, pair.public_key().as_ref().to_vec()); + assert!(public.verify(b"x", sig.as_ref()).is_err(), "ed25519 verifies another message"); + + // RFC 6979, appendix A.2.5: P-256, SHA-256, the message "sample". + let public = signature::UnparsedPublicKey::new( + &signature::ECDSA_P256_SHA256_FIXED, + hex("0460fed4ba255a9d31c961eb74c6356d68c049b8923b61fa6ce669622e60f29fb6\ + 7903fe1008b8bc99a41ae9e95628bc64f2f1b20c2d7e9f5177a3c294d4462299"), + ); + let sig = hex( + "efd48b2aacb6a8fd1140dd9cd45e81d69d2c877b56aaf991c34d0ea84eaf3716\ + f7cb1c942d657c41d436c7a1b6e29f65f3e900dbb9aff4064dc4ab2f843acda8", + ); + public.verify(b"sample", &sig).expect("ecdsa p-256 rfc6979 a.2.5 verifies"); + assert!(public.verify(b"samplf", &sig).is_err(), "ecdsa p-256 verifies another message"); + println!("ring_kat: ecdsa p-256 rfc6979 a.2.5 verified, and refused for another message"); + + // RFC 6979, appendix A.2.6: P-384, SHA-384, the message "sample". + let public = signature::UnparsedPublicKey::new( + &signature::ECDSA_P384_SHA384_FIXED, + hex("04ec3a4e415b4e19a4568618029f427fa5da9a8bc4ae92e02e06aae5286b300c64\ + def8f0ea9055866064a254515480bc13\ + 8015d9b72d7d57244ea8ef9ac0c621896708a59367f9dfb9f54ca84b3f1c9db1\ + 288b231c3ae0d4fe7344fd2533264720"), + ); + verifies("ecdsa p-384 rfc6979 a.2.6", b"sample", hex( + "94edbb92a5ecb8aad4736e56c691916b3f88140666ce9fa73d64c4ea95ad133c\ + 81a648152e44acf96e36dd1e80fabe46\ + 99ef4aeb15f178cea1fe40db2603138f130e740a19624526203b6351d0a3a94f\ + a329c145786e679e7b82c71a38628ac8", + ), |m, s| public.verify(m, s)); + + // NIST CAVP's FIPS 186-3 RSA SigVer files, `[mod = 2048]`, SHA256: the + // first vector of each whose result is P. + let pkcs1 = signature::RsaPublicKeyComponents { + n: hex("c47abacc2a84d56f3614d92fd62ed36ddde459664b9301dcd1d61781cfcc026b\ + cb2399bee7e75681a80b7bf500e2d08ceae1c42ec0b707927f2b2fe92ae85208\ + 7d25f1d260cc74905ee5f9b254ed05494a9fe06732c3680992dd6f0dc634568d\ + 11542a705f83ae96d2a49763d5fbb24398edf3702bc94bc168190166492b8671\ + de874bb9cecb058c6c8344aa8c93754d6effcd44a41ed7de0a9dcd9144437f21\ + 2b18881d042d331a4618a9e630ef9bb66305e4fdf8f0391b3b2313fe549f0189\ + ff968b92f33c266a4bc2cffc897d1937eeb9e406f5d0eaa7a14782e76af3fce9\ + 8f54ed237b4a04a4159a5f6250a296a902880204e61d891c4da29f2d65f34cbb"), + e: hex("49d2a1"), + }; + verifies("rsa pkcs#1 v1.5 sha-256 2048 cavp SigVer15", &hex( + "95123c8d1b236540b86976a11cea31f8bd4e6c54c235147d20ce722b03a6ad75\ + 6fbd918c27df8ea9ce3104444c0bbe877305bc02e35535a02a58dcda306e632a\ + d30b3dc3ce0ba97fdf46ec192965dd9cd7f4a71b02b8cba3d442646eeec4af59\ + 0824ca98d74fbca934d0b6867aa1991f3040b707e806de6e66b5934f05509bea", + ), hex( + "51265d96f11ab338762891cb29bf3f1d2b3305107063f5f3245af376dfcc7027\ + d39365de70a31db05e9e10eb6148cb7f6425f0c93c4fb0e2291adbd22c77656a\ + fc196858a11e1c670d9eeb592613e69eb4f3aa501730743ac4464486c7ae68fd\ + 509e896f63884e9424f69c1c5397959f1e52a368667a598a1fc90125273d9341\ + 295d2f8e1cc4969bf228c860e07a3546be2eeda1cde48ee94d062801fe666e4a\ + 7ae8cb9cd79262c017b081af874ff00453ca43e34efdb43fffb0bb42a4e2d32a\ + 5e5cc9e8546a221fe930250e5f5333e0efe58ffebf19369a3b8ae5a67f6a048b\ + c9ef915bda25160729b508667ada84a0c27e7e26cf2abca413e5e4693f4a9405", + ), |m, s| pkcs1.verify(&signature::RSA_PKCS1_2048_8192_SHA256, m, s)); + + // Its salt is 32 bytes, the one length `ring` verifies with SHA-256. + let pss = signature::RsaPublicKeyComponents { + n: hex("a47d04e7cacdba4ea26eca8a4c6e14563c2ce03b623b768c0d49868a57121301\ + dbf783d82f4c055e73960e70550187d0af62ac3496f0a3d9103c2eb7919a7275\ + 2fa7ce8c688d81e3aee99468887a15288afbb7acb845b7c522b5c64e678fcd3d\ + 22feb84b44272700be527d2b2025a3f83c2383bf6a39cf5b4e48b3cf2f56eef0\ + dfff18555e31037b915248694876f3047814415164f2c660881e694b58c28038\ + a032ad25634aad7b39171dee368e3d59bfb7299e4601d4587e68caaf8db457b7\ + 5af42fc0cf1ae7caced286d77fac6cedb03ad94f1433d2c94d08e60bc1fdef05\ + 43cd2951e765b38230fdd18de5d2ca627ddc032fe05bbd2ff21e2db1c2f94d8b"), + e: hex("10e43f"), + }; + verifies("rsa-pss sha-256 2048 cavp SigVerPSS", &hex( + "e002377affb04f0fe4598de9d92d31d6c786040d5776976556a2cfc55e54a1dc\ + b3cb1b126bd6a4bed2a184990ccea773fcc79d246553e6c64f686d21ad415267\ + 3cafec22aeb40f6a084e8a5b4991f4c64cf8a927effd0fd775e71e8329e41fdd\ + 4457b3911173187b4f09a817d79ea2397fc12dfe3d9c9a0290c8ead31b6690a6", + ), hex( + "4f9b425c2058460e4ab2f5c96384da2327fd29150f01955a76b4efe956af06dc\ + 08779a374ee4607eab61a93adc5608f4ec36e47f2a0f754e8ff839a8a19b1db1\ + e884ea4cf348cd455069eb87afd53645b44e28a0a56808f5031da5ba9112768d\ + fbfca44ebe63a0c0572b731d66122fb71609be1480faa4e4f75e43955159d70f\ + 081e2a32fbb19a48b9f162cf6b2fb445d2d6994bc58910a26b5943477803cdaa\ + a1bd74b0da0a5d053d8b1dc593091db5388383c26079f344e2aea600d0e32416\ + 4b450f7b9b465111b7265f3b1b063089ae7e2623fc0fda8052cf4bf3379102fb\ + f71d7c98e8258664ceed637d20f95ff0111881e650ce61f251d9c3a629ef222d", + ), |m, s| pss.verify(&signature::RSA_PSS_2048_8192_SHA256, m, s)); +} + +/// Verify `sig` over `message`, then refuse it with one bit of the signature +/// flipped. +fn verifies(what: &str, message: &[u8], sig: Vec, verify: impl Fn(&[u8], &[u8]) -> Result<(), ring::error::Unspecified>) { + verify(message, &sig).unwrap_or_else(|_| panic!("{what} does not verify")); + let mut forged = sig; + forged[0] ^= 1; + assert!(verify(message, &forged).is_err(), "{what} verifies with a flipped signature bit"); + println!("ring_kat: {what} verified, and refused with a flipped signature bit"); +} + +fn system_random() { + use rand::SecureRandom; + let rng = rand::SystemRandom::new(); + let (mut a, mut b) = ([0u8; 32], [0u8; 32]); + rng.fill(&mut a).expect("the system's random bytes"); + rng.fill(&mut b).expect("the system's random bytes"); + assert_ne!(a, b, "two fills of SystemRandom are the same 32 bytes"); + println!("ring_kat: SystemRandom filled two different 32-byte buffers"); +} + +fn main() { + digests(); + hmac_sha256(); + x25519(); + aeads(); + signatures(); + system_random(); + println!("ring_kat: ok"); +} diff --git a/tests/toyos.rs b/tests/toyos.rs index 562ca05ee8a..6297f0b73f8 100644 --- a/tests/toyos.rs +++ b/tests/toyos.rs @@ -145,6 +145,12 @@ const RUST_SKIP: &[&str] = &[ // It needs a host that dials its listeners when it says they wait: // `libc_sockets` runs it on `tests/netcase`. "nodelay_accepted", + // It needs a NIC in front of netstack and HTTPS servers behind it: + // `https_fetch` runs it on `tests/netcase`. + "https_get", + // `https_fetch` runs it on the boot its fetches run on, so that test + // carries its own oracle; a shared run would be the same QEMU CPU twice. + "ring_kat", // It needs a host peer that ends each stream as the stream asks: // `libc_sockets` runs it on `tests/netcase`. "stream_ends_std", @@ -306,6 +312,11 @@ const MACHINE_TESTS: &[&str] = &[ // kernel's pipes and the library read together, netstack has no host // build, and the T14's bench has no peer that answers or resets on cue. "libc_sockets", + // An HTTPS client's fetch from a peer on the host, and its refusals: + // `ring`'s ToyOS build, the random source and wall clock it trusts a + // certificate by and the sockets under it exist in a ToyOS guest alone, + // and the T14's row is the next stage's, against a server on the bench. + "https_fetch", // The nested-NMI report is a raw write to the 16550, which the T14 does not // have. "nested_nmi_is_loud", @@ -3390,6 +3401,92 @@ fn libc_sockets() -> Result<(), String> { Ok(()) } +/// An unchanged HTTPS client — `ureq` on `rustls` on `ring`, as published but +/// for `ring`'s ToyOS arms — trusting `build::trust_roots` and this run's +/// authority, and sending the project's `User-Agent`, on one boot of +/// `tests/netcase` against servers this run starts on the host: it fetches a body over TLS 1.3 whole, by a hash over +/// every byte the host computes with another SHA-256 than the guest's; and it +/// refuses a certificate for another address and one from an authority the +/// roots file does not hold, each by its own name, at the handshake. First, +/// on the same CPU, `ring_kat` holds `ring` to its specifications' answers: +/// the server is `rustls` on `ring` too, so the handshake alone would pass a +/// `ring` wrong at both ends. +fn https_fetch() -> Result<(), String> { + use common::https::{self, Authority, Seen, Server}; + use sha2::Digest; + const JOB: &str = "https_get"; + const KAT: &str = "ring_kat"; + /// Root `CLAUDE.md`'s, spelled again here so the guest's copy is checked + /// against the rule and not against itself. + const USER_AGENT: &str = "toyos-build (https://github.com/ToyOSOrg/ToyOS)"; + const WAIT: Duration = Duration::from_secs(30); + let host: std::net::IpAddr = https::HOST.parse().expect("an address"); + let trusted = Authority::new("ToyOS harness test authority"); + let stranger = Authority::new("ToyOS harness authority nothing trusts"); + let body = std::sync::Arc::new(https::body()); + let want = format!("{JOB}: ok bytes={} sha256={:x}", body.len(), sha2::Sha256::digest(body.as_slice())); + let fetched = Server::start(trusted.leaf(host), body.clone())?; + let wrong_name = Server::start(trusted.leaf([192, 0, 2, 1].into()), body.clone())?; + let untrusted = Server::start(stranger.leaf(host), body)?; + + // The shipped roots and the harness's authority, under a name of the + // test's own: the client reads the file its argument names. + const ROOTS: &str = "etc/ssl/harness-cert.pem"; + let mut roots = toyos_build::build::trust_roots(); + roots.extend_from_slice(trusted.pem().as_bytes()); + let crate_path = compile::repo_root().join("tests/toyos-rust-tests"); + let bins = [JOB, KAT].map(|name| (name.to_string(), qemu::build_toyos_bin(qemu::SUITE_ARCH, &crate_path, name))); + let options = BootOptions { extra_root_files: vec![(ROOTS.to_string(), roots)], ..Default::default() }; + let mut qemu = boot_netcase(&[], &bins, options)?; + let kat = qemu.run_test("test_rs_ring_kat", Duration::from_secs(120)); + if kat.error.is_some() || kat.exit_code != Some(0) || !kat.stdout.lines().any(|l| l.trim_end() == "ring_kat: ok") { + return Err(format!("{KAT} ended {:?} ({:?}):\n{}", kat.exit_code, kat.error, kat.stdout)); + } + let roots = format!("/system/{ROOTS}"); + let fetches: [(&str, &Server, String, i32); 3] = [ + ("the trusted server", &fetched, want, 0), + ("the server named for another address", &wrong_name, format!("{JOB}: refused not-valid-for-name"), 2), + ("the server no root vouches for", &untrusted, format!("{JOB}: refused unknown-issuer"), 2), + ]; + for (what, server, line, code) in &fetches { + let url = format!("https://{}:{}/body", https::HOST, server.port); + let result = qemu.run_test(&format!("test_rs_{JOB} {url} {roots}"), Duration::from_secs(120)); + if let Some(why) = &result.error { + return Err(format!("{what}: {why}\nthe job said:\n{}", result.stdout)); + } + if result.exit_code != Some(*code) || !result.stdout.lines().any(|l| l.trim_end() == line) { + return Err(format!( + "{what}: the client ended {:?} and was to end {code} saying {line:?}:\n{}", + result.exit_code, result.stdout + )); + } + } + + // Of a refused handshake the server sees the connection arrive and no + // more: this client sends it no alert, on the host as here, and the close + // after it need not arrive + // (`issues/a-guest-close-after-a-refused-handshake-can-leave-its-peer-waiting.md`). + for (what, server, _, code) in &fetches { + if !matches!(server.next(WAIT).map_err(|e| format!("{what}: {e}"))?, Seen::Accepted) { + return Err(format!("{what} saw something before a connection")); + } + if *code != 0 { + continue; + } + match server.next(WAIT).map_err(|e| format!("{what}: {e}"))? { + Seen::Served { version: Some(rustls::ProtocolVersion::TLSv1_3), path, user_agents } + if path == "/body" && user_agents == [USER_AGENT] => {} + other => return Err(format!("{what} saw {other:?}, not a TLS 1.3 GET of /body as {USER_AGENT:?}")), + } + } + for (what, server, _, _) in &fetches { + if let Some(seen) = server.more().into_iter().find(|seen| !matches!(seen, Seen::Failed)) { + return Err(format!("{what} saw more than the one connection it was asked for: {seen:?}")); + } + } + Ok(()) +} + /// Run the machine-shape test, which owns its QEMU: the machine shape *is* the /// test. fn run_machine_test(name: &str, test_config: &Path) -> Result<(), String> { @@ -3399,6 +3496,7 @@ fn run_machine_test(name: &str, test_config: &Path) -> Result<(), String> { "netstack_streams" => netstack_streams(qemu::Profile::Headless), "netstack_streams_e1000e" => netstack_streams(qemu::Profile::HeadlessE1000e), "libc_sockets" => libc_sockets(), + "https_fetch" => https_fetch(), "nested_nmi_is_loud" => faults::nested_nmi_is_loud(test_config), "machine_shutdown" => power::machine_shutdown(test_config), "acpi_power_button" => power::acpi_power_button(test_config), diff --git a/userland/doom/Cargo.toml b/userland/doom/Cargo.toml index 49a54583d97..103f8e36ed6 100644 --- a/userland/doom/Cargo.toml +++ b/userland/doom/Cargo.toml @@ -18,9 +18,7 @@ softbuffer = { version = "0.4", default-features = false, features = ["wayland", [build-dependencies] cc = "1.1" -ureq = { version = "3", default-features = false, features = ["rustls-no-provider", "rustls-webpki-roots"] } -rustls-rustcrypto = "0.0.2-alpha" -webpki-roots = "1" +ureq = { version = "3", default-features = false, features = ["rustls"] } flate2 = { version = "1", default-features = false, features = ["rust_backend"] } tar = "0.4" diff --git a/userland/doom/build.rs b/userland/doom/build.rs index d7b494f4a3a..6e33658bece 100644 --- a/userland/doom/build.rs +++ b/userland/doom/build.rs @@ -1,5 +1,4 @@ use std::path::PathBuf; -use std::sync::Arc; use std::{fs, path::Path}; /// The upstream doomgeneric commit `/system/bin/doom` is built from. @@ -158,21 +157,12 @@ fn main() { println!("cargo:rerun-if-changed=doomgeneric"); } -fn http_agent() -> ureq::Agent { - let tls = ureq::tls::TlsConfig::builder() - .provider(ureq::tls::TlsProvider::Rustls) - .root_certs(ureq::tls::RootCerts::WebPki) - .unversioned_rustls_crypto_provider(Arc::new(rustls_rustcrypto::provider())) - .build(); - ureq::Agent::config_builder() - .tls_config(tls) - .build() - .new_agent() -} +/// Root `CLAUDE.md`'s: the only `User-Agent` ToyOS sends. +const USER_AGENT: &str = "toyos-build (https://github.com/ToyOSOrg/ToyOS)"; fn download_doomgeneric(root: &Path) { println!("Downloading doomgeneric {DOOMGENERIC_COMMIT}..."); - let agent = http_agent(); + let agent = ureq::Agent::config_builder().user_agent(USER_AGENT).build().new_agent(); // The commit is the checksum: GitHub's archive of a sha is that sha's tree // and can be nothing else, so pinning the URL needs no second hash of ours. let resp = agent