From 8dbc13f5b30bbba76a1b5ad244fee5c5f93be4ac Mon Sep 17 00:00:00 2001 From: Tom Gouville Date: Tue, 7 Nov 2023 16:58:58 +0100 Subject: [PATCH 01/63] update bindgen to 0.69.1 --- Cargo.lock | 149 ++++++++++-------------------- crates/libssh-sys/Cargo.toml | 2 +- crates/openssl-src-111/Cargo.toml | 2 +- crates/wolfssl-src/Cargo.toml | 4 +- crates/wolfssl-src/src/lib.rs | 1 + sshpuffin/Cargo.toml | 2 +- tlspuffin-claims/Cargo.toml | 2 +- 7 files changed, 55 insertions(+), 107 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index a94f90d07..b9107f8cf 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -169,34 +169,11 @@ dependencies = [ [[package]] name = "bindgen" -version = "0.60.1" +version = "0.69.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "062dddbc1ba4aca46de6338e2bf87771414c335f7b2f2036e8f3e9befebf88e6" +checksum = "9ffcebc3849946a7170a05992aac39da343a90676ab392c51a4280981d6379c2" dependencies = [ - "bitflags", - "cexpr", - "clang-sys", - "clap 3.2.23", - "env_logger 0.9.3", - "lazy_static", - "lazycell", - "log", - "peeking_take_while", - "proc-macro2", - "quote", - "regex", - "rustc-hash", - "shlex", - "which", -] - -[[package]] -name = "bindgen" -version = "0.65.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cfdf7b466f9a4903edc73f95d6d2bcd5baf8ae620638762244d3f60143643cc5" -dependencies = [ - "bitflags", + "bitflags 2.4.1", "cexpr", "clang-sys", "lazy_static", @@ -209,7 +186,7 @@ dependencies = [ "regex", "rustc-hash", "shlex", - "syn 2.0.15", + "syn 2.0.39", "which", ] @@ -219,6 +196,12 @@ version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" +[[package]] +name = "bitflags" +version = "2.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "327762f6e5a765692301e5bb513e0d9fef63be86bbc14528052b1cd3e6f03e07" + [[package]] name = "block-buffer" version = "0.10.4" @@ -334,26 +317,11 @@ version = "2.34.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a0610544180c38b88101fecf2dd634b174a62eef6946f84dfc6a7127512b381c" dependencies = [ - "bitflags", - "textwrap 0.11.0", + "bitflags 1.3.2", + "textwrap", "unicode-width", ] -[[package]] -name = "clap" -version = "3.2.23" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "71655c45cb9845d3270c9d6df84ebe72b4dad3c2ba3f7023ad47c144e4e473a5" -dependencies = [ - "atty", - "bitflags", - "clap_lex 0.2.4", - "indexmap", - "strsim", - "termcolor", - "textwrap 0.16.0", -] - [[package]] name = "clap" version = "4.2.4" @@ -371,21 +339,12 @@ checksum = "84080e799e54cff944f4b4a4b0e71630b0e0443b25b985175c7dddc1a859b749" dependencies = [ "anstream", "anstyle", - "bitflags", - "clap_lex 0.4.1", + "bitflags 1.3.2", + "clap_lex", "once_cell", "strsim", ] -[[package]] -name = "clap_lex" -version = "0.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2850f2f5a82cbf437dd5af4d49848fbdfc27c157c3d010345776f952765261c5" -dependencies = [ - "os_str_bytes", -] - [[package]] name = "clap_lex" version = "0.4.1" @@ -538,7 +497,7 @@ version = "0.25.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e64e6c0fbe2c17357405f7c758c1ef960fce08bdfb2c03d88d2a18d7e09c4b67" dependencies = [ - "bitflags", + "bitflags 1.3.2", "crossterm_winapi", "libc", "mio", @@ -554,7 +513,7 @@ version = "0.26.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a84cda67535339806297f1b331d6dd6320470d2a0fe65381e79ee9e156dd3d13" dependencies = [ - "bitflags", + "bitflags 1.3.2", "crossterm_winapi", "libc", "mio", @@ -638,7 +597,7 @@ dependencies = [ "proc-macro2", "quote", "scratch", - "syn 2.0.15", + "syn 2.0.39", ] [[package]] @@ -655,7 +614,7 @@ checksum = "2345488264226bf682893e25de0769f3360aac9957980ec49361b083ddaa5bc5" dependencies = [ "proc-macro2", "quote", - "syn 2.0.15", + "syn 2.0.39", ] [[package]] @@ -812,7 +771,7 @@ checksum = "1a5c6c585bc94aaf2c7b51dd4c2ba22680844aba4c687be581871a6f518c5742" dependencies = [ "proc-macro2", "quote", - "syn 2.0.15", + "syn 2.0.39", ] [[package]] @@ -883,7 +842,7 @@ checksum = "89ca545a94061b6365f2c7355b4b32bd20df3ff95f02da9329b34ccc3bd6ee72" dependencies = [ "proc-macro2", "quote", - "syn 2.0.15", + "syn 2.0.39", ] [[package]] @@ -1214,7 +1173,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f560e150e1686a0bc4ec3af1cff46b916b32b0fc42c15732ed800e0ac8689b4c" dependencies = [ "quote", - "syn 2.0.15", + "syn 2.0.39", ] [[package]] @@ -1264,7 +1223,7 @@ dependencies = [ name = "libssh-sys" version = "0.1.0" dependencies = [ - "bindgen 0.60.1", + "bindgen", "cmake", ] @@ -1423,7 +1382,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f346ff70e7dbfd675fe90590b92d59ef2de15a8779ae305ebcbfd3f0caf59be4" dependencies = [ "autocfg", - "bitflags", + "bitflags 1.3.2", "cfg-if", "libc", "memoffset 0.6.5", @@ -1436,7 +1395,7 @@ version = "0.26.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bfdda3d196821d6af13126e40375cdf7da646a96114af134d5f417a9a1dc8e1a" dependencies = [ - "bitflags", + "bitflags 1.3.2", "cfg-if", "libc", "memoffset 0.7.1", @@ -1530,7 +1489,7 @@ version = "0.10.50" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7e30d8bc91859781f0a943411186324d580f2bbeb71b452fe91ae344806af3f1" dependencies = [ - "bitflags", + "bitflags 1.3.2", "cfg-if", "foreign-types 0.3.2", "libc", @@ -1547,14 +1506,14 @@ checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.15", + "syn 2.0.39", ] [[package]] name = "openssl-src" version = "111.22.0+1.1.1q" dependencies = [ - "bindgen 0.60.1", + "bindgen", "cc", "libressl-src", "security-claims", @@ -1582,12 +1541,6 @@ dependencies = [ "num-traits", ] -[[package]] -name = "os_str_bytes" -version = "6.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ceedf44fb00f2d1984b0bc98102627ce622e083e49a5bacdb3e514fa4238e267" - [[package]] name = "parking_lot" version = "0.12.1" @@ -1682,19 +1635,19 @@ checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" [[package]] name = "prettyplease" -version = "0.2.4" +version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ceca8aaf45b5c46ec7ed39fff75f57290368c1846d33d24a122ca81416ab058" +checksum = "ae005bd773ab59b4725093fd7df83fd7892f7d8eafb48dbd7de6e024e4215f9d" dependencies = [ "proc-macro2", - "syn 2.0.15", + "syn 2.0.39", ] [[package]] name = "proc-macro2" -version = "1.0.56" +version = "1.0.69" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b63bdb0cd06f1f4dedf69b254734f9b45af66e4a031e42a7480257d9898b435" +checksum = "134c189feb4956b20f6f547d2cf727d4c0fe06722b20a0eec87ed445a97f92da" dependencies = [ "unicode-ident", ] @@ -1726,9 +1679,9 @@ dependencies = [ [[package]] name = "quote" -version = "1.0.26" +version = "1.0.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4424af4bf778aae2051a77b60283332f386554255d722233d09fbfc7e30da2fc" +checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae" dependencies = [ "proc-macro2", ] @@ -1797,7 +1750,7 @@ version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a" dependencies = [ - "bitflags", + "bitflags 1.3.2", ] [[package]] @@ -1806,7 +1759,7 @@ version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "567664f262709473930a4bf9e51bf2ebf3348f2e748ccc50dea20646858f8f29" dependencies = [ - "bitflags", + "bitflags 1.3.2", ] [[package]] @@ -1868,7 +1821,7 @@ version = "0.37.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "722529a737f5a942fdbac3a46cee213053196737c5eaa3386d52e85b786f2659" dependencies = [ - "bitflags", + "bitflags 1.3.2", "errno", "io-lifetimes", "libc", @@ -1923,7 +1876,7 @@ dependencies = [ name = "security-claims" version = "0.1.0" dependencies = [ - "bindgen 0.60.1", + "bindgen", "hex", "itertools", ] @@ -1971,7 +1924,7 @@ checksum = "291a097c63d8497e00160b166a967a4a79c64f3facdd01cbd7502231688d77df" dependencies = [ "proc-macro2", "quote", - "syn 2.0.15", + "syn 2.0.39", ] [[package]] @@ -2113,8 +2066,8 @@ dependencies = [ name = "sshpuffin" version = "0.1.0" dependencies = [ - "bindgen 0.65.1", - "bitflags", + "bindgen", + "bitflags 1.3.2", "byteorder", "cmake", "digest", @@ -2164,9 +2117,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.15" +version = "2.0.39" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a34fcf3e8b60f57e6a14301a2e916d323af98b0ea63c599441eec8558660c822" +checksum = "23e78b90f2fcf45d3e842032ce32e3f2d1545ba6636271dcbf24fa306d87be7a" dependencies = [ "proc-macro2", "quote", @@ -2215,12 +2168,6 @@ dependencies = [ "unicode-width", ] -[[package]] -name = "textwrap" -version = "0.16.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "222a222a5bfe1bba4a77b45ec488a741b3cb8872e5e499451fd7d0129c9c7c3d" - [[package]] name = "thiserror" version = "1.0.40" @@ -2238,7 +2185,7 @@ checksum = "f9456a42c5b0d803c8cd86e73dd7cc9edd429499f37a3550d286d5e86720569f" dependencies = [ "proc-macro2", "quote", - "syn 2.0.15", + "syn 2.0.39", ] [[package]] @@ -2330,7 +2277,7 @@ checksum = "61a573bdc87985e9d6ddeed1b3d864e8a302c847e40d647746df2f1de209d1ce" dependencies = [ "proc-macro2", "quote", - "syn 2.0.15", + "syn 2.0.39", ] [[package]] @@ -2339,7 +2286,7 @@ version = "0.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ccdd26cbd674007e649a272da4475fb666d3aa0ad0531da7136db6fab0e5bad1" dependencies = [ - "bitflags", + "bitflags 1.3.2", "cassowary", "crossterm 0.25.0", "unicode-segmentation", @@ -2749,7 +2696,7 @@ checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a" name = "wolfssl" version = "0.1.0" dependencies = [ - "bitflags", + "bitflags 1.3.2", "foreign-types 0.5.0", "libc", "log", @@ -2761,7 +2708,7 @@ name = "wolfssl-src" version = "0.1.0" dependencies = [ "autotools", - "bindgen 0.60.1", + "bindgen", "cfg-if", ] diff --git a/crates/libssh-sys/Cargo.toml b/crates/libssh-sys/Cargo.toml index 0d76a1f09..5e0638c81 100644 --- a/crates/libssh-sys/Cargo.toml +++ b/crates/libssh-sys/Cargo.toml @@ -14,4 +14,4 @@ vendored-libssh0104 = [] [build-dependencies] cmake = "0.1.48" -bindgen = "0.60.1" +bindgen = "0.69.1" diff --git a/crates/openssl-src-111/Cargo.toml b/crates/openssl-src-111/Cargo.toml index ac869ac4e..8ec4626b2 100644 --- a/crates/openssl-src-111/Cargo.toml +++ b/crates/openssl-src-111/Cargo.toml @@ -64,6 +64,6 @@ exclude = ['target'] [dependencies] cc = "1.0" -bindgen = "0.60.1" +bindgen = "0.69.1" security-claims = "0.1.0" libressl-src = { version = "111.0.0", optional = true } diff --git a/crates/wolfssl-src/Cargo.toml b/crates/wolfssl-src/Cargo.toml index 13af22f18..576e8834b 100644 --- a/crates/wolfssl-src/Cargo.toml +++ b/crates/wolfssl-src/Cargo.toml @@ -4,10 +4,10 @@ version = "0.1.0" edition = "2021" [dependencies] -bindgen = "0.60.1" +bindgen = "0.69.1" autotools = "0.2.6" cfg-if = "1.0.0" [[bin]] name = "build_static" -path = "src/main.rs" \ No newline at end of file +path = "src/main.rs" diff --git a/crates/wolfssl-src/src/lib.rs b/crates/wolfssl-src/src/lib.rs index 4ad9daa37..6672c94e7 100644 --- a/crates/wolfssl-src/src/lib.rs +++ b/crates/wolfssl-src/src/lib.rs @@ -193,6 +193,7 @@ pub fn build(options: &WolfSSLOptions) -> std::io::Result<()> { let ignored_macros = IgnoreMacros(ignored_macros); let bindings = bindgen::Builder::default() + .size_t_is_usize(false) .header(format!("{}/wrapper.h", options.source_dir.display())) .header(format!("{}/wolfssl/internal.h", out_dir.display())) .clang_arg(format!("-I{}/include/", out_dir.display())) diff --git a/sshpuffin/Cargo.toml b/sshpuffin/Cargo.toml index 25fe93c5d..0333686b0 100644 --- a/sshpuffin/Cargo.toml +++ b/sshpuffin/Cargo.toml @@ -36,4 +36,4 @@ test-log = "0.2.11" [build-dependencies] cmake = "0.1.48" -bindgen = "0.65.1" +bindgen = "0.69.1" diff --git a/tlspuffin-claims/Cargo.toml b/tlspuffin-claims/Cargo.toml index 587cf6a1d..859fa4446 100644 --- a/tlspuffin-claims/Cargo.toml +++ b/tlspuffin-claims/Cargo.toml @@ -10,4 +10,4 @@ hex = "0.4.3" itertools = "0.10.3" [build-dependencies] -bindgen = "0.60.1" +bindgen = "0.69.1" From 4c530d6acdb3bb422aa6c4c1244137aff56927aa Mon Sep 17 00:00:00 2001 From: Tom Gouville Date: Sat, 11 Nov 2023 16:46:02 +0100 Subject: [PATCH 02/63] crates to build and bind boringssl --- .gitignore | 2 + Cargo.lock | 189 +- Cargo.toml | 3 + crates/boring/Cargo.toml | 71 + crates/boring/LICENSE | 16 + crates/boring/README.md | 1 + crates/boring/examples/fips_enabled.rs | 3 + crates/boring/examples/mk_certs.rs | 160 + crates/boring/src/aes.rs | 210 + crates/boring/src/asn1.rs | 705 +++ crates/boring/src/base64.rs | 134 + crates/boring/src/bio.rs | 74 + crates/boring/src/bn.rs | 1308 ++++++ crates/boring/src/conf.rs | 39 + crates/boring/src/derive.rs | 133 + crates/boring/src/dh.rs | 133 + crates/boring/src/dsa.rs | 357 ++ crates/boring/src/ec.rs | 1017 +++++ crates/boring/src/ecdsa.rs | 135 + crates/boring/src/error.rs | 266 ++ crates/boring/src/ex_data.rs | 32 + crates/boring/src/fips.rs | 21 + crates/boring/src/hash.rs | 496 +++ crates/boring/src/lib.rs | 184 + crates/boring/src/macros.rs | 261 ++ crates/boring/src/memcmp.rs | 92 + crates/boring/src/nid.rs | 1123 +++++ crates/boring/src/pkcs12.rs | 287 ++ crates/boring/src/pkcs5.rs | 302 ++ crates/boring/src/pkey.rs | 618 +++ crates/boring/src/rand.rs | 52 + crates/boring/src/rsa.rs | 872 ++++ crates/boring/src/sha.rs | 531 +++ crates/boring/src/sign.rs | 680 +++ crates/boring/src/srtp.rs | 59 + crates/boring/src/ssl/bio.rs | 246 + crates/boring/src/ssl/callbacks.rs | 463 ++ crates/boring/src/ssl/connector.rs | 446 ++ crates/boring/src/ssl/error.rs | 213 + crates/boring/src/ssl/mod.rs | 3954 +++++++++++++++++ crates/boring/src/ssl/test/mod.rs | 1046 +++++ .../boring/src/ssl/test/private_key_method.rs | 281 ++ crates/boring/src/ssl/test/server.rs | 202 + crates/boring/src/ssl/test/session.rs | 186 + crates/boring/src/stack.rs | 375 ++ crates/boring/src/string.rs | 84 + crates/boring/src/symm.rs | 964 ++++ crates/boring/src/util.rs | 67 + crates/boring/src/version.rs | 111 + crates/boring/src/x509/extension.rs | 531 +++ crates/boring/src/x509/mod.rs | 1700 +++++++ crates/boring/src/x509/store.rs | 112 + crates/boring/src/x509/tests.rs | 458 ++ crates/boring/src/x509/verify.rs | 87 + crates/boring/test/alt_name_cert.pem | 22 + crates/boring/test/cert.pem | 19 + crates/boring/test/certs.pem | 40 + crates/boring/test/cms.p12 | Bin 0 -> 1709 bytes crates/boring/test/cms_pubkey.der | Bin 0 -> 688 bytes crates/boring/test/dhparams.pem | 8 + crates/boring/test/dsa.pem | 12 + crates/boring/test/dsa.pem.pub | 12 + crates/boring/test/dsaparam.pem | 9 + crates/boring/test/identity.p12 | Bin 0 -> 3386 bytes crates/boring/test/key.der | Bin 0 -> 1193 bytes crates/boring/test/key.der.pub | Bin 0 -> 294 bytes crates/boring/test/key.pem | 28 + crates/boring/test/key.pem.pub | 9 + crates/boring/test/keystore-empty-chain.p12 | Bin 0 -> 2514 bytes crates/boring/test/nid_test_cert.pem | 12 + crates/boring/test/nid_uid_test_cert.pem | 24 + crates/boring/test/pkcs1.pem.pub | 8 + crates/boring/test/pkcs8-nocrypt.der | Bin 0 -> 1216 bytes crates/boring/test/pkcs8.der | Bin 0 -> 1298 bytes crates/boring/test/root-ca.key | 27 + crates/boring/test/root-ca.pem | 21 + crates/boring/test/rsa-encrypted.pem | 30 + crates/boring/test/rsa.pem | 27 + crates/boring/test/rsa.pem.pub | 9 + crates/boringssl-src/Cargo.toml | 17 + crates/boringssl-src/boringssl/.gitkeep | 0 .../patches/fix-CVE-2022-25638.patch | 190 + .../patches/fix-CVE-2022-25640.patch | 126 + .../patches/fix-CVE-2022-39173.patch | 62 + .../patches/fix-CVE-2022-42905.patch | 462 ++ crates/boringssl-src/src/lib.rs | 155 + crates/boringssl-src/src/main.rs | 28 + crates/boringssl-src/wrapper.h | 3 + crates/boringssl-sys/Cargo.toml | 23 + crates/boringssl-sys/build.rs | 94 + crates/boringssl-sys/src/lib.rs | 89 + 91 files changed, 23623 insertions(+), 35 deletions(-) create mode 100644 crates/boring/Cargo.toml create mode 100644 crates/boring/LICENSE create mode 120000 crates/boring/README.md create mode 100644 crates/boring/examples/fips_enabled.rs create mode 100644 crates/boring/examples/mk_certs.rs create mode 100644 crates/boring/src/aes.rs create mode 100644 crates/boring/src/asn1.rs create mode 100644 crates/boring/src/base64.rs create mode 100644 crates/boring/src/bio.rs create mode 100644 crates/boring/src/bn.rs create mode 100644 crates/boring/src/conf.rs create mode 100644 crates/boring/src/derive.rs create mode 100644 crates/boring/src/dh.rs create mode 100644 crates/boring/src/dsa.rs create mode 100644 crates/boring/src/ec.rs create mode 100644 crates/boring/src/ecdsa.rs create mode 100644 crates/boring/src/error.rs create mode 100644 crates/boring/src/ex_data.rs create mode 100644 crates/boring/src/fips.rs create mode 100644 crates/boring/src/hash.rs create mode 100644 crates/boring/src/lib.rs create mode 100644 crates/boring/src/macros.rs create mode 100644 crates/boring/src/memcmp.rs create mode 100644 crates/boring/src/nid.rs create mode 100644 crates/boring/src/pkcs12.rs create mode 100644 crates/boring/src/pkcs5.rs create mode 100644 crates/boring/src/pkey.rs create mode 100644 crates/boring/src/rand.rs create mode 100644 crates/boring/src/rsa.rs create mode 100644 crates/boring/src/sha.rs create mode 100644 crates/boring/src/sign.rs create mode 100644 crates/boring/src/srtp.rs create mode 100644 crates/boring/src/ssl/bio.rs create mode 100644 crates/boring/src/ssl/callbacks.rs create mode 100644 crates/boring/src/ssl/connector.rs create mode 100644 crates/boring/src/ssl/error.rs create mode 100644 crates/boring/src/ssl/mod.rs create mode 100644 crates/boring/src/ssl/test/mod.rs create mode 100644 crates/boring/src/ssl/test/private_key_method.rs create mode 100644 crates/boring/src/ssl/test/server.rs create mode 100644 crates/boring/src/ssl/test/session.rs create mode 100644 crates/boring/src/stack.rs create mode 100644 crates/boring/src/string.rs create mode 100644 crates/boring/src/symm.rs create mode 100644 crates/boring/src/util.rs create mode 100644 crates/boring/src/version.rs create mode 100644 crates/boring/src/x509/extension.rs create mode 100644 crates/boring/src/x509/mod.rs create mode 100644 crates/boring/src/x509/store.rs create mode 100644 crates/boring/src/x509/tests.rs create mode 100644 crates/boring/src/x509/verify.rs create mode 100644 crates/boring/test/alt_name_cert.pem create mode 100644 crates/boring/test/cert.pem create mode 100644 crates/boring/test/certs.pem create mode 100644 crates/boring/test/cms.p12 create mode 100644 crates/boring/test/cms_pubkey.der create mode 100644 crates/boring/test/dhparams.pem create mode 100644 crates/boring/test/dsa.pem create mode 100644 crates/boring/test/dsa.pem.pub create mode 100644 crates/boring/test/dsaparam.pem create mode 100644 crates/boring/test/identity.p12 create mode 100644 crates/boring/test/key.der create mode 100644 crates/boring/test/key.der.pub create mode 100644 crates/boring/test/key.pem create mode 100644 crates/boring/test/key.pem.pub create mode 100644 crates/boring/test/keystore-empty-chain.p12 create mode 100644 crates/boring/test/nid_test_cert.pem create mode 100644 crates/boring/test/nid_uid_test_cert.pem create mode 100644 crates/boring/test/pkcs1.pem.pub create mode 100644 crates/boring/test/pkcs8-nocrypt.der create mode 100644 crates/boring/test/pkcs8.der create mode 100644 crates/boring/test/root-ca.key create mode 100644 crates/boring/test/root-ca.pem create mode 100644 crates/boring/test/rsa-encrypted.pem create mode 100644 crates/boring/test/rsa.pem create mode 100644 crates/boring/test/rsa.pem.pub create mode 100644 crates/boringssl-src/Cargo.toml create mode 100644 crates/boringssl-src/boringssl/.gitkeep create mode 100644 crates/boringssl-src/patches/fix-CVE-2022-25638.patch create mode 100644 crates/boringssl-src/patches/fix-CVE-2022-25640.patch create mode 100644 crates/boringssl-src/patches/fix-CVE-2022-39173.patch create mode 100644 crates/boringssl-src/patches/fix-CVE-2022-42905.patch create mode 100644 crates/boringssl-src/src/lib.rs create mode 100644 crates/boringssl-src/src/main.rs create mode 100644 crates/boringssl-src/wrapper.h create mode 100644 crates/boringssl-sys/Cargo.toml create mode 100644 crates/boringssl-sys/build.rs create mode 100644 crates/boringssl-sys/src/lib.rs diff --git a/.gitignore b/.gitignore index d1df18b24..c344d79b0 100644 --- a/.gitignore +++ b/.gitignore @@ -38,3 +38,5 @@ coverage.csv *.html *.css stats.json.should-be-empty.log + +/crates/boringssl-src/boringssl/ diff --git a/Cargo.lock b/Cargo.lock index a94f90d07..05db10867 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -173,7 +173,7 @@ version = "0.60.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "062dddbc1ba4aca46de6338e2bf87771414c335f7b2f2036e8f3e9befebf88e6" dependencies = [ - "bitflags", + "bitflags 1.3.2", "cexpr", "clang-sys", "clap 3.2.23", @@ -196,7 +196,7 @@ version = "0.65.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cfdf7b466f9a4903edc73f95d6d2bcd5baf8ae620638762244d3f60143643cc5" dependencies = [ - "bitflags", + "bitflags 1.3.2", "cexpr", "clang-sys", "lazy_static", @@ -209,7 +209,30 @@ dependencies = [ "regex", "rustc-hash", "shlex", - "syn 2.0.15", + "syn 2.0.39", + "which", +] + +[[package]] +name = "bindgen" +version = "0.69.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ffcebc3849946a7170a05992aac39da343a90676ab392c51a4280981d6379c2" +dependencies = [ + "bitflags 2.4.1", + "cexpr", + "clang-sys", + "lazy_static", + "lazycell", + "log", + "peeking_take_while", + "prettyplease", + "proc-macro2", + "quote", + "regex", + "rustc-hash", + "shlex", + "syn 2.0.39", "which", ] @@ -219,6 +242,12 @@ version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" +[[package]] +name = "bitflags" +version = "2.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "327762f6e5a765692301e5bb513e0d9fef63be86bbc14528052b1cd3e6f03e07" + [[package]] name = "block-buffer" version = "0.10.4" @@ -228,6 +257,35 @@ dependencies = [ "generic-array", ] +[[package]] +name = "boring" +version = "4.0.0" +dependencies = [ + "bitflags 2.4.1", + "boringssl-sys", + "foreign-types 0.5.0", + "hex", + "libc", + "once_cell", + "rusty-hook", +] + +[[package]] +name = "boringssl-src" +version = "0.1.0" +dependencies = [ + "bindgen 0.69.1", + "cmake", +] + +[[package]] +name = "boringssl-sys" +version = "0.1.0" +dependencies = [ + "bindgen 0.69.1", + "boringssl-src", +] + [[package]] name = "bumpalo" version = "3.12.0" @@ -317,6 +375,15 @@ dependencies = [ "winapi", ] +[[package]] +name = "ci_info" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24f638c70e8c5753795cc9a8c07c44da91554a09e4cf11a7326e8161b0a3c45e" +dependencies = [ + "envmnt", +] + [[package]] name = "clang-sys" version = "1.6.1" @@ -334,7 +401,7 @@ version = "2.34.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a0610544180c38b88101fecf2dd634b174a62eef6946f84dfc6a7127512b381c" dependencies = [ - "bitflags", + "bitflags 1.3.2", "textwrap 0.11.0", "unicode-width", ] @@ -346,7 +413,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "71655c45cb9845d3270c9d6df84ebe72b4dad3c2ba3f7023ad47c144e4e473a5" dependencies = [ "atty", - "bitflags", + "bitflags 1.3.2", "clap_lex 0.2.4", "indexmap", "strsim", @@ -371,7 +438,7 @@ checksum = "84080e799e54cff944f4b4a4b0e71630b0e0443b25b985175c7dddc1a859b749" dependencies = [ "anstream", "anstyle", - "bitflags", + "bitflags 1.3.2", "clap_lex 0.4.1", "once_cell", "strsim", @@ -538,7 +605,7 @@ version = "0.25.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e64e6c0fbe2c17357405f7c758c1ef960fce08bdfb2c03d88d2a18d7e09c4b67" dependencies = [ - "bitflags", + "bitflags 1.3.2", "crossterm_winapi", "libc", "mio", @@ -554,7 +621,7 @@ version = "0.26.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a84cda67535339806297f1b331d6dd6320470d2a0fe65381e79ee9e156dd3d13" dependencies = [ - "bitflags", + "bitflags 1.3.2", "crossterm_winapi", "libc", "mio", @@ -638,7 +705,7 @@ dependencies = [ "proc-macro2", "quote", "scratch", - "syn 2.0.15", + "syn 2.0.39", ] [[package]] @@ -655,7 +722,7 @@ checksum = "2345488264226bf682893e25de0769f3360aac9957980ec49361b083ddaa5bc5" dependencies = [ "proc-macro2", "quote", - "syn 2.0.15", + "syn 2.0.39", ] [[package]] @@ -730,6 +797,16 @@ dependencies = [ "termcolor", ] +[[package]] +name = "envmnt" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2d328fc287c61314c4a61af7cfdcbd7e678e39778488c7cb13ec133ce0f4059" +dependencies = [ + "fsio", + "indexmap", +] + [[package]] name = "erased-serde" version = "0.3.25" @@ -812,7 +889,7 @@ checksum = "1a5c6c585bc94aaf2c7b51dd4c2ba22680844aba4c687be581871a6f518c5742" dependencies = [ "proc-macro2", "quote", - "syn 2.0.15", + "syn 2.0.39", ] [[package]] @@ -827,6 +904,12 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "aa9a19cbb55df58761df49b23516a86d432839add4af60fc256da840f66ed35b" +[[package]] +name = "fsio" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1fd087255f739f4f1aeea69f11b72f8080e9c2e7645cd06955dad4a178a49e3" + [[package]] name = "futures" version = "0.3.28" @@ -883,7 +966,7 @@ checksum = "89ca545a94061b6365f2c7355b4b32bd20df3ff95f02da9329b34ccc3bd6ee72" dependencies = [ "proc-macro2", "quote", - "syn 2.0.15", + "syn 2.0.39", ] [[package]] @@ -926,6 +1009,15 @@ dependencies = [ "version_check", ] +[[package]] +name = "getopts" +version = "0.2.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "14dbbfd5c71d70241ecf9e6f13737f7b5ce823821063188d7e46c41d371eebd5" +dependencies = [ + "unicode-width", +] + [[package]] name = "getrandom" version = "0.2.9" @@ -1214,7 +1306,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f560e150e1686a0bc4ec3af1cff46b916b32b0fc42c15732ed800e0ac8689b4c" dependencies = [ "quote", - "syn 2.0.15", + "syn 2.0.39", ] [[package]] @@ -1416,6 +1508,12 @@ dependencies = [ "windows-sys 0.45.0", ] +[[package]] +name = "nias" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab250442c86f1850815b5d268639dff018c0627022bc1940eb2d642ca1ce12f0" + [[package]] name = "nix" version = "0.25.1" @@ -1423,7 +1521,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f346ff70e7dbfd675fe90590b92d59ef2de15a8779ae305ebcbfd3f0caf59be4" dependencies = [ "autocfg", - "bitflags", + "bitflags 1.3.2", "cfg-if", "libc", "memoffset 0.6.5", @@ -1436,7 +1534,7 @@ version = "0.26.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bfdda3d196821d6af13126e40375cdf7da646a96114af134d5f417a9a1dc8e1a" dependencies = [ - "bitflags", + "bitflags 1.3.2", "cfg-if", "libc", "memoffset 0.7.1", @@ -1530,7 +1628,7 @@ version = "0.10.50" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7e30d8bc91859781f0a943411186324d580f2bbeb71b452fe91ae344806af3f1" dependencies = [ - "bitflags", + "bitflags 1.3.2", "cfg-if", "foreign-types 0.3.2", "libc", @@ -1547,7 +1645,7 @@ checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.15", + "syn 2.0.39", ] [[package]] @@ -1682,19 +1780,19 @@ checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" [[package]] name = "prettyplease" -version = "0.2.4" +version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ceca8aaf45b5c46ec7ed39fff75f57290368c1846d33d24a122ca81416ab058" +checksum = "ae005bd773ab59b4725093fd7df83fd7892f7d8eafb48dbd7de6e024e4215f9d" dependencies = [ "proc-macro2", - "syn 2.0.15", + "syn 2.0.39", ] [[package]] name = "proc-macro2" -version = "1.0.56" +version = "1.0.69" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b63bdb0cd06f1f4dedf69b254734f9b45af66e4a031e42a7480257d9898b435" +checksum = "134c189feb4956b20f6f547d2cf727d4c0fe06722b20a0eec87ed445a97f92da" dependencies = [ "unicode-ident", ] @@ -1726,9 +1824,9 @@ dependencies = [ [[package]] name = "quote" -version = "1.0.26" +version = "1.0.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4424af4bf778aae2051a77b60283332f386554255d722233d09fbfc7e30da2fc" +checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae" dependencies = [ "proc-macro2", ] @@ -1797,7 +1895,7 @@ version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a" dependencies = [ - "bitflags", + "bitflags 1.3.2", ] [[package]] @@ -1806,7 +1904,7 @@ version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "567664f262709473930a4bf9e51bf2ebf3348f2e748ccc50dea20646858f8f29" dependencies = [ - "bitflags", + "bitflags 1.3.2", ] [[package]] @@ -1868,7 +1966,7 @@ version = "0.37.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "722529a737f5a942fdbac3a46cee213053196737c5eaa3386d52e85b786f2659" dependencies = [ - "bitflags", + "bitflags 1.3.2", "errno", "io-lifetimes", "libc", @@ -1882,6 +1980,18 @@ version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4f3208ce4d8448b3f3e7d168a73f5e0c43a61e32930de3bceeccedb388b6bf06" +[[package]] +name = "rusty-hook" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96cee9be61be7e1cbadd851e58ed7449c29c620f00b23df937cb9cbc04ac21a3" +dependencies = [ + "ci_info", + "getopts", + "nias", + "toml", +] + [[package]] name = "ryu" version = "1.0.13" @@ -1971,7 +2081,7 @@ checksum = "291a097c63d8497e00160b166a967a4a79c64f3facdd01cbd7502231688d77df" dependencies = [ "proc-macro2", "quote", - "syn 2.0.15", + "syn 2.0.39", ] [[package]] @@ -2114,7 +2224,7 @@ name = "sshpuffin" version = "0.1.0" dependencies = [ "bindgen 0.65.1", - "bitflags", + "bitflags 1.3.2", "byteorder", "cmake", "digest", @@ -2164,9 +2274,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.15" +version = "2.0.39" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a34fcf3e8b60f57e6a14301a2e916d323af98b0ea63c599441eec8558660c822" +checksum = "23e78b90f2fcf45d3e842032ce32e3f2d1545ba6636271dcbf24fa306d87be7a" dependencies = [ "proc-macro2", "quote", @@ -2238,7 +2348,7 @@ checksum = "f9456a42c5b0d803c8cd86e73dd7cc9edd429499f37a3550d286d5e86720569f" dependencies = [ "proc-macro2", "quote", - "syn 2.0.15", + "syn 2.0.39", ] [[package]] @@ -2330,7 +2440,16 @@ checksum = "61a573bdc87985e9d6ddeed1b3d864e8a302c847e40d647746df2f1de209d1ce" dependencies = [ "proc-macro2", "quote", - "syn 2.0.15", + "syn 2.0.39", +] + +[[package]] +name = "toml" +version = "0.5.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4f7f0dd8d50a853a531c426359045b1998f04219d88799810762cd4ad314234" +dependencies = [ + "serde", ] [[package]] @@ -2339,7 +2458,7 @@ version = "0.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ccdd26cbd674007e649a272da4475fb666d3aa0ad0531da7136db6fab0e5bad1" dependencies = [ - "bitflags", + "bitflags 1.3.2", "cassowary", "crossterm 0.25.0", "unicode-segmentation", @@ -2749,7 +2868,7 @@ checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a" name = "wolfssl" version = "0.1.0" dependencies = [ - "bitflags", + "bitflags 1.3.2", "foreign-types 0.5.0", "libc", "log", diff --git a/Cargo.toml b/Cargo.toml index bec4d5702..c1d784525 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,6 +12,9 @@ members = [ "crates/wolfssl-sys", "crates/wolfssl-src", "crates/libssh-sys", + "crates/boringssl-src", + "crates/boringssl-sys", + "crates/boring" ] exclude = [ "crates/openssl-src-111" ] diff --git a/crates/boring/Cargo.toml b/crates/boring/Cargo.toml new file mode 100644 index 000000000..76c632214 --- /dev/null +++ b/crates/boring/Cargo.toml @@ -0,0 +1,71 @@ +[package] +name = "boring" +version = "4.0.0" +authors = ["Steven Fackler ", "Ivan Nikulin "] +license = "Apache-2.0" +description = "BoringSSL bindings" +documentation = "https://docs.rs/boring" +readme = "README.md" +keywords = ["crypto", "tls", "ssl", "dtls"] +categories = ["cryptography", "api-bindings"] +edition = "2021" + +# [package.metadata.docs.rs] +# features = ["rpk", "pq-experimental"] +# rustdoc-args = ["--cfg", "docsrs"] + +# [features] +# Controlling the build + +# Use a FIPS-validated version of boringssl. +# fips = ["boring-sys/fips"] + +# Link with precompiled FIPS-validated `bcm.o` module. +# fips-link-precompiled = ["boring-sys/fips-link-precompiled"] + +# Enables Raw public key API (https://datatracker.ietf.org/doc/html/rfc7250) +# This feature is necessary in order to compile the bindings for the +# default branch of boringSSL. Alternatively, a version of boringSSL that +# implements the same feature set can be provided by setting +# `BORING_BSSL{,_FIPS}_SOURCE_PATH` and `BORING_BSSL{,_FIPS}_ASSUME_PATCHED`. +# rpk = ["boring-sys/rpk"] + +# Applies a patch to the boringSSL source code that enables support for PQ key +# exchange. This feature is necessary in order to compile the bindings for the +# default branch of boringSSL. Alternatively, a version of boringSSL that +# implements the same feature set can be provided by setting +# `BORING_BSSL{,_FIPS}_SOURCE_PATH` and `BORING_BSSL{,_FIPS}_ASSUME_PATCHED`. +# pq-experimental = ["boring-sys/pq-experimental"] + +# Controlling key exchange preferences at compile time + +# Choose key exchange preferences at compile time. This prevents the user from +# choosing their own preferences. +# kx-safe-default = [] + +# Support PQ key exchange. The client will prefer classical key exchange, but +# will upgrade to PQ key exchange if requested by the server. This is the +# safest option if you don't know if the peer supports PQ key exchange. This +# feature implies "kx-safe-default". +# kx-client-pq-supported = ["kx-safe-default"] + +# Prefer PQ key exchange. The client will prefer PQ exchange, but fallback to +# classical key exchange if requested by the server. This is the best option if +# you know the peer supports PQ key exchange. This feature implies +# "kx-safe-default" and "kx-client-pq-supported". +# kx-client-pq-preferred = ["kx-safe-default", "kx-client-pq-supported"] + +# Disable key exchange involving non-NIST key exchange on the client side. +# Implies "kx-safe-default". +# kx-client-nist-required = ["kx-safe-default"] + +[dependencies] +bitflags = "2.4" +foreign-types = "0.5" +once_cell = "1.0" +libc = "0.2" +boringssl-sys = { path = "../boringssl-sys" } + +[dev-dependencies] +hex = "0.4" +rusty-hook = "^0.11" diff --git a/crates/boring/LICENSE b/crates/boring/LICENSE new file mode 100644 index 000000000..2ce594941 --- /dev/null +++ b/crates/boring/LICENSE @@ -0,0 +1,16 @@ +Copyright 2011-2017 Google Inc. + 2013 Jack Lloyd + 2013-2014 Steven Fackler + 2020 Ivan Nikulin + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/crates/boring/README.md b/crates/boring/README.md new file mode 120000 index 000000000..32d46ee88 --- /dev/null +++ b/crates/boring/README.md @@ -0,0 +1 @@ +../README.md \ No newline at end of file diff --git a/crates/boring/examples/fips_enabled.rs b/crates/boring/examples/fips_enabled.rs new file mode 100644 index 000000000..7a57aee4f --- /dev/null +++ b/crates/boring/examples/fips_enabled.rs @@ -0,0 +1,3 @@ +fn main() { + println!("boring::fips::enabled(): {}", boring::fips::enabled()); +} diff --git a/crates/boring/examples/mk_certs.rs b/crates/boring/examples/mk_certs.rs new file mode 100644 index 000000000..ce0c8492d --- /dev/null +++ b/crates/boring/examples/mk_certs.rs @@ -0,0 +1,160 @@ +//! A program that generates ca certs, certs verified by the ca, and public +//! and private keys. + +extern crate boring; + +use boring::asn1::Asn1Time; +use boring::bn::{BigNum, MsbOption}; +use boring::error::ErrorStack; +use boring::hash::MessageDigest; +use boring::pkey::{PKey, PKeyRef, Private}; +use boring::rsa::Rsa; +use boring::x509::extension::{ + AuthorityKeyIdentifier, BasicConstraints, KeyUsage, SubjectAlternativeName, + SubjectKeyIdentifier, +}; +use boring::x509::{X509NameBuilder, X509Ref, X509Req, X509ReqBuilder, X509}; + +/// Make a CA certificate and private key +fn mk_ca_cert() -> Result<(X509, PKey), ErrorStack> { + let rsa = Rsa::generate(2048)?; + let privkey = PKey::from_rsa(rsa)?; + + let mut x509_name = X509NameBuilder::new()?; + x509_name.append_entry_by_text("C", "US")?; + x509_name.append_entry_by_text("ST", "TX")?; + x509_name.append_entry_by_text("O", "Some CA organization")?; + x509_name.append_entry_by_text("CN", "ca test")?; + let x509_name = x509_name.build(); + + let mut cert_builder = X509::builder()?; + cert_builder.set_version(2)?; + let serial_number = { + let mut serial = BigNum::new()?; + serial.rand(159, MsbOption::MAYBE_ZERO, false)?; + serial.to_asn1_integer()? + }; + cert_builder.set_serial_number(&serial_number)?; + cert_builder.set_subject_name(&x509_name)?; + cert_builder.set_issuer_name(&x509_name)?; + cert_builder.set_pubkey(&privkey)?; + let not_before = Asn1Time::days_from_now(0)?; + cert_builder.set_not_before(¬_before)?; + let not_after = Asn1Time::days_from_now(365)?; + cert_builder.set_not_after(¬_after)?; + + cert_builder.append_extension(BasicConstraints::new().critical().ca().build()?)?; + cert_builder.append_extension( + KeyUsage::new() + .critical() + .key_cert_sign() + .crl_sign() + .build()?, + )?; + + let subject_key_identifier = + SubjectKeyIdentifier::new().build(&cert_builder.x509v3_context(None, None))?; + cert_builder.append_extension(subject_key_identifier)?; + + cert_builder.sign(&privkey, MessageDigest::sha256())?; + let cert = cert_builder.build(); + + Ok((cert, privkey)) +} + +/// Make a X509 request with the given private key +fn mk_request(privkey: &PKey) -> Result { + let mut req_builder = X509ReqBuilder::new()?; + req_builder.set_pubkey(privkey)?; + + let mut x509_name = X509NameBuilder::new()?; + x509_name.append_entry_by_text("C", "US")?; + x509_name.append_entry_by_text("ST", "TX")?; + x509_name.append_entry_by_text("O", "Some organization")?; + x509_name.append_entry_by_text("CN", "www.example.com")?; + let x509_name = x509_name.build(); + req_builder.set_subject_name(&x509_name)?; + + req_builder.sign(privkey, MessageDigest::sha256())?; + let req = req_builder.build(); + Ok(req) +} + +/// Make a certificate and private key signed by the given CA cert and private key +fn mk_ca_signed_cert( + ca_cert: &X509Ref, + ca_privkey: &PKeyRef, +) -> Result<(X509, PKey), ErrorStack> { + let rsa = Rsa::generate(2048)?; + let privkey = PKey::from_rsa(rsa)?; + + let req = mk_request(&privkey)?; + + let mut cert_builder = X509::builder()?; + cert_builder.set_version(2)?; + let serial_number = { + let mut serial = BigNum::new()?; + serial.rand(159, MsbOption::MAYBE_ZERO, false)?; + serial.to_asn1_integer()? + }; + cert_builder.set_serial_number(&serial_number)?; + cert_builder.set_subject_name(req.subject_name())?; + cert_builder.set_issuer_name(ca_cert.subject_name())?; + cert_builder.set_pubkey(&privkey)?; + let not_before = Asn1Time::days_from_now(0)?; + cert_builder.set_not_before(¬_before)?; + let not_after = Asn1Time::days_from_now(365)?; + cert_builder.set_not_after(¬_after)?; + + cert_builder.append_extension(BasicConstraints::new().build()?)?; + + cert_builder.append_extension( + KeyUsage::new() + .critical() + .non_repudiation() + .digital_signature() + .key_encipherment() + .build()?, + )?; + + let subject_key_identifier = + SubjectKeyIdentifier::new().build(&cert_builder.x509v3_context(Some(ca_cert), None))?; + cert_builder.append_extension(subject_key_identifier)?; + + let auth_key_identifier = AuthorityKeyIdentifier::new() + .keyid(false) + .issuer(false) + .build(&cert_builder.x509v3_context(Some(ca_cert), None))?; + cert_builder.append_extension(auth_key_identifier)?; + + let subject_alt_name = SubjectAlternativeName::new() + .dns("*.example.com") + .dns("hello.com") + .build(&cert_builder.x509v3_context(Some(ca_cert), None))?; + cert_builder.append_extension(subject_alt_name)?; + + cert_builder.sign(ca_privkey, MessageDigest::sha256())?; + let cert = cert_builder.build(); + + Ok((cert, privkey)) +} + +fn real_main() -> Result<(), ErrorStack> { + let (ca_cert, ca_privkey) = mk_ca_cert()?; + let (cert, _privkey) = mk_ca_signed_cert(&ca_cert, &ca_privkey)?; + + // Verify that this cert was issued by this ca + match ca_cert.issued(&cert) { + Ok(()) => println!("Certificate verified!"), + Err(ver_err) => println!("Failed to verify certificate: {}", ver_err), + }; + + Ok(()) +} + +fn main() { + match real_main() { + Ok(()) => println!("Finished."), + Err(e) => println!("Error: {}", e), + }; +} diff --git a/crates/boring/src/aes.rs b/crates/boring/src/aes.rs new file mode 100644 index 000000000..12288763e --- /dev/null +++ b/crates/boring/src/aes.rs @@ -0,0 +1,210 @@ +//! Low level AES IGE and key wrapping functionality +//! +//! AES ECB, CBC, XTS, CTR, CFB, GCM and other conventional symmetric encryption +//! modes are found in [`symm`]. This is the implementation of AES IGE and key wrapping +//! +//! Advanced Encryption Standard (AES) provides symmetric key cipher that +//! the same key is used to encrypt and decrypt data. This implementation +//! uses 128, 192, or 256 bit keys. This module provides functions to +//! create a new key with [`new_encrypt`]. +//! +//! [`new_encrypt`]: struct.AesKey.html#method.new_encrypt +//! +//! The [`symm`] module should be used in preference to this module in most cases. +//! The IGE block cypher is a non-traditional cipher mode. More traditional AES +//! encryption methods are found in the [`Crypter`] and [`Cipher`] structs. +//! +//! [`symm`]: ../symm/index.html +//! [`Crypter`]: ../symm/struct.Crypter.html +//! [`Cipher`]: ../symm/struct.Cipher.html +//! +//! # Examples +//! +//! ## Key wrapping +//! ```rust +//! use boring::aes::{AesKey, unwrap_key, wrap_key}; +//! +//! let kek = b"\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F"; +//! let key_to_wrap = b"\x00\x11\x22\x33\x44\x55\x66\x77\x88\x99\xAA\xBB\xCC\xDD\xEE\xFF"; +//! +//! let enc_key = AesKey::new_encrypt(kek).unwrap(); +//! let mut ciphertext = [0u8; 24]; +//! wrap_key(&enc_key, None, &mut ciphertext, &key_to_wrap[..]).unwrap(); +//! let dec_key = AesKey::new_decrypt(kek).unwrap(); +//! let mut orig_key = [0u8; 16]; +//! unwrap_key(&dec_key, None, &mut orig_key, &ciphertext[..]).unwrap(); +//! +//! assert_eq!(&orig_key[..], &key_to_wrap[..]); +//! ``` +//! +use crate::ffi; +use libc::{c_int, c_uint, size_t}; +use std::mem::MaybeUninit; +use std::ptr; + +/// Provides Error handling for parsing keys. +#[derive(Debug)] +pub struct KeyError(()); + +/// The key used to encrypt or decrypt cipher blocks. +pub struct AesKey(ffi::AES_KEY); + +impl AesKey { + /// Prepares a key for encryption. + /// + /// # Failure + /// + /// Returns an error if the key is not 128, 192, or 256 bits. + #[allow(deprecated)] // https://github.com/rust-lang/rust/issues/63566 + pub fn new_encrypt(key: &[u8]) -> Result { + unsafe { + assert!(key.len() <= c_int::max_value() as usize / 8); + + let mut aes_key = MaybeUninit::uninit(); + let r = ffi::AES_set_encrypt_key( + key.as_ptr() as *const _, + key.len() as c_uint * 8, + aes_key.as_mut_ptr(), + ); + if r == 0 { + Ok(AesKey(aes_key.assume_init())) + } else { + Err(KeyError(())) + } + } + } + + /// Prepares a key for decryption. + /// + /// # Failure + /// + /// Returns an error if the key is not 128, 192, or 256 bits. + #[allow(deprecated)] // https://github.com/rust-lang/rust/issues/63566 + pub fn new_decrypt(key: &[u8]) -> Result { + unsafe { + assert!(key.len() <= c_int::max_value() as usize / 8); + + let mut aes_key = MaybeUninit::uninit(); + let r = ffi::AES_set_decrypt_key( + key.as_ptr() as *const _, + key.len() as c_uint * 8, + aes_key.as_mut_ptr(), + ); + + if r == 0 { + Ok(AesKey(aes_key.assume_init())) + } else { + Err(KeyError(())) + } + } + } +} + +/// Wrap a key, according to [RFC 3394](https://tools.ietf.org/html/rfc3394) +/// +/// * `key`: The key-encrypting-key to use. Must be a encrypting key +/// * `iv`: The IV to use. You must use the same IV for both wrapping and unwrapping +/// * `out`: The output buffer to store the ciphertext +/// * `in_`: The input buffer, storing the key to be wrapped +/// +/// Returns the number of bytes written into `out` +/// +/// # Panics +/// +/// Panics if either `out` or `in_` do not have sizes that are a multiple of 8, or if +/// `out` is not 8 bytes longer than `in_` +pub fn wrap_key( + key: &AesKey, + iv: Option<[u8; 8]>, + out: &mut [u8], + in_: &[u8], +) -> Result { + unsafe { + assert!(out.len() >= in_.len() + 8); // Ciphertext is 64 bits longer (see 2.2.1) + + let written = ffi::AES_wrap_key( + &key.0 as *const _ as *mut _, // this is safe, the implementation only uses the key as a const pointer. + iv.as_ref() + .map_or(ptr::null(), |iv| iv.as_ptr() as *const _), + out.as_ptr() as *mut _, + in_.as_ptr() as *const _, + in_.len() as size_t, + ); + if written <= 0 { + Err(KeyError(())) + } else { + Ok(written as usize) + } + } +} + +/// Unwrap a key, according to [RFC 3394](https://tools.ietf.org/html/rfc3394) +/// +/// * `key`: The key-encrypting-key to decrypt the wrapped key. Must be a decrypting key +/// * `iv`: The same IV used for wrapping the key +/// * `out`: The buffer to write the unwrapped key to +/// * `in_`: The input ciphertext +/// +/// Returns the number of bytes written into `out` +/// +/// # Panics +/// +/// Panics if either `out` or `in_` do not have sizes that are a multiple of 8, or +/// if `in_` is not 8 bytes longer than `out` +pub fn unwrap_key( + key: &AesKey, + iv: Option<[u8; 8]>, + out: &mut [u8], + in_: &[u8], +) -> Result { + unsafe { + assert!(out.len() + 8 <= in_.len()); + + let written = ffi::AES_unwrap_key( + &key.0 as *const _ as *mut _, // this is safe, the implementation only uses the key as a const pointer. + iv.as_ref() + .map_or(ptr::null(), |iv| iv.as_ptr() as *const _), + out.as_ptr() as *mut _, + in_.as_ptr() as *const _, + in_.len() as size_t, + ); + + if written <= 0 { + Err(KeyError(())) + } else { + Ok(written as usize) + } + } +} + +#[cfg(test)] +mod test { + use hex::FromHex; + + use super::*; + + // from the RFC https://tools.ietf.org/html/rfc3394#section-2.2.3 + #[test] + fn test_wrap_unwrap() { + let raw_key = Vec::from_hex("000102030405060708090A0B0C0D0E0F").unwrap(); + let key_data = Vec::from_hex("00112233445566778899AABBCCDDEEFF").unwrap(); + let expected_ciphertext = + Vec::from_hex("1FA68B0A8112B447AEF34BD8FB5A7B829D3E862371D2CFE5").unwrap(); + + let enc_key = AesKey::new_encrypt(&raw_key).unwrap(); + let mut wrapped = [0; 24]; + assert_eq!( + wrap_key(&enc_key, None, &mut wrapped, &key_data).unwrap(), + 24 + ); + assert_eq!(&wrapped[..], &expected_ciphertext[..]); + + let dec_key = AesKey::new_decrypt(&raw_key).unwrap(); + let mut unwrapped = [0; 16]; + assert_eq!( + unwrap_key(&dec_key, None, &mut unwrapped, &wrapped).unwrap(), + 16 + ); + assert_eq!(&unwrapped[..], &key_data[..]); + } +} diff --git a/crates/boring/src/asn1.rs b/crates/boring/src/asn1.rs new file mode 100644 index 000000000..02e601fcf --- /dev/null +++ b/crates/boring/src/asn1.rs @@ -0,0 +1,705 @@ +#![deny(missing_docs)] + +//! Defines the format of certificiates +//! +//! This module is used by [`x509`] and other certificate building functions +//! to describe time, strings, and objects. +//! +//! Abstract Syntax Notation One is an interface description language. +//! The specification comes from [X.208] by OSI, and rewritten in X.680. +//! ASN.1 describes properties of an object with a type set. Those types +//! can be atomic, structured, choice, and other (CHOICE and ANY). These +//! types are expressed as a number and the assignment operator ::= gives +//! the type a name. +//! +//! The implementation here provides a subset of the ASN.1 types that OpenSSL +//! uses, especially in the properties of a certificate used in HTTPS. +//! +//! [X.208]: https://www.itu.int/rec/T-REC-X.208-198811-W/en +//! [`x509`]: ../x509/struct.X509Builder.html +//! +//! ## Examples +//! +//! ``` +//! use boring::asn1::Asn1Time; +//! let tomorrow = Asn1Time::days_from_now(1); +//! ``` +use crate::ffi; +use foreign_types::{ForeignType, ForeignTypeRef}; +use libc::{c_char, c_int, c_long, time_t}; +use std::cmp::Ordering; +use std::ffi::CString; +use std::fmt; +use std::ptr; +use std::slice; +use std::str; + +use crate::bio::MemBio; +use crate::bn::{BigNum, BigNumRef}; +use crate::error::ErrorStack; +use crate::nid::Nid; +use crate::stack::Stackable; +use crate::string::OpensslString; +use crate::{cvt, cvt_p}; + +foreign_type_and_impl_send_sync! { + type CType = ffi::ASN1_GENERALIZEDTIME; + fn drop = ffi::ASN1_GENERALIZEDTIME_free; + + /// Non-UTC representation of time + /// + /// If a time can be represented by UTCTime, UTCTime is used + /// otherwise, ASN1_GENERALIZEDTIME is used. This would be, for + /// example outside the year range of 1950-2049. + /// + /// [ASN1_GENERALIZEDTIME_set] documentation from OpenSSL provides + /// further details of implmentation. Note: these docs are from the master + /// branch as documentation on the 1.1.0 branch did not include this page. + /// + /// [ASN1_GENERALIZEDTIME_set]: https://www.openssl.org/docs/manmaster/man3/ASN1_GENERALIZEDTIME_set.html + pub struct Asn1GeneralizedTime; +} + +impl fmt::Display for Asn1GeneralizedTimeRef { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + unsafe { + let mem_bio = match MemBio::new() { + Err(_) => return f.write_str("error"), + Ok(m) => m, + }; + let print_result = cvt(ffi::ASN1_GENERALIZEDTIME_print( + mem_bio.as_ptr(), + self.as_ptr(), + )); + match print_result { + Err(_) => f.write_str("error"), + Ok(_) => f.write_str(str::from_utf8_unchecked(mem_bio.get_buf())), + } + } + } +} + +/// The type of an ASN.1 value. +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +pub struct Asn1Type(c_int); + +#[allow(missing_docs)] // no need to document the constants +impl Asn1Type { + pub const EOC: Asn1Type = Asn1Type(ffi::V_ASN1_EOC); + + pub const BOOLEAN: Asn1Type = Asn1Type(ffi::V_ASN1_BOOLEAN); + + pub const INTEGER: Asn1Type = Asn1Type(ffi::V_ASN1_INTEGER); + + pub const BIT_STRING: Asn1Type = Asn1Type(ffi::V_ASN1_BIT_STRING); + + pub const OCTET_STRING: Asn1Type = Asn1Type(ffi::V_ASN1_OCTET_STRING); + + pub const NULL: Asn1Type = Asn1Type(ffi::V_ASN1_NULL); + + pub const OBJECT: Asn1Type = Asn1Type(ffi::V_ASN1_OBJECT); + + pub const OBJECT_DESCRIPTOR: Asn1Type = Asn1Type(ffi::V_ASN1_OBJECT_DESCRIPTOR); + + pub const EXTERNAL: Asn1Type = Asn1Type(ffi::V_ASN1_EXTERNAL); + + pub const REAL: Asn1Type = Asn1Type(ffi::V_ASN1_REAL); + + pub const ENUMERATED: Asn1Type = Asn1Type(ffi::V_ASN1_ENUMERATED); + + pub const UTF8STRING: Asn1Type = Asn1Type(ffi::V_ASN1_UTF8STRING); + + pub const SEQUENCE: Asn1Type = Asn1Type(ffi::V_ASN1_SEQUENCE); + + pub const SET: Asn1Type = Asn1Type(ffi::V_ASN1_SET); + + pub const NUMERICSTRING: Asn1Type = Asn1Type(ffi::V_ASN1_NUMERICSTRING); + + pub const PRINTABLESTRING: Asn1Type = Asn1Type(ffi::V_ASN1_PRINTABLESTRING); + + pub const T61STRING: Asn1Type = Asn1Type(ffi::V_ASN1_T61STRING); + + pub const TELETEXSTRING: Asn1Type = Asn1Type(ffi::V_ASN1_TELETEXSTRING); + + pub const VIDEOTEXSTRING: Asn1Type = Asn1Type(ffi::V_ASN1_VIDEOTEXSTRING); + + pub const IA5STRING: Asn1Type = Asn1Type(ffi::V_ASN1_IA5STRING); + + pub const UTCTIME: Asn1Type = Asn1Type(ffi::V_ASN1_UTCTIME); + + pub const GENERALIZEDTIME: Asn1Type = Asn1Type(ffi::V_ASN1_GENERALIZEDTIME); + + pub const GRAPHICSTRING: Asn1Type = Asn1Type(ffi::V_ASN1_GRAPHICSTRING); + + pub const ISO64STRING: Asn1Type = Asn1Type(ffi::V_ASN1_ISO64STRING); + + pub const VISIBLESTRING: Asn1Type = Asn1Type(ffi::V_ASN1_VISIBLESTRING); + + pub const GENERALSTRING: Asn1Type = Asn1Type(ffi::V_ASN1_GENERALSTRING); + + pub const UNIVERSALSTRING: Asn1Type = Asn1Type(ffi::V_ASN1_UNIVERSALSTRING); + + pub const BMPSTRING: Asn1Type = Asn1Type(ffi::V_ASN1_BMPSTRING); + + /// Constructs an `Asn1Type` from a raw OpenSSL value. + pub fn from_raw(value: c_int) -> Self { + Asn1Type(value) + } + + /// Returns the raw OpenSSL value represented by this type. + pub fn as_raw(&self) -> c_int { + self.0 + } +} + +/// Difference between two ASN1 times. +/// +/// This `struct` is created by the [`diff`] method on [`Asn1TimeRef`]. See its +/// documentation for more. +/// +/// [`diff`]: struct.Asn1TimeRef.html#method.diff +/// [`Asn1TimeRef`]: struct.Asn1TimeRef.html +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct TimeDiff { + /// Difference in days + pub days: c_int, + /// Difference in seconds. + /// + /// This is always less than the number of seconds in a day. + pub secs: c_int, +} + +foreign_type_and_impl_send_sync! { + type CType = ffi::ASN1_TIME; + fn drop = ffi::ASN1_TIME_free; + /// Time storage and comparison + /// + /// Asn1Time should be used to store and share time information + /// using certificates. If Asn1Time is set using a string, it must + /// be in either YYMMDDHHMMSSZ, YYYYMMDDHHMMSSZ, or another ASN.1 format. + /// + /// [ASN_TIME_set] documentation at OpenSSL explains the ASN.1 implementation + /// used by OpenSSL. + /// + /// [ASN_TIME_set]: https://www.openssl.org/docs/man1.1.0/crypto/ASN1_TIME_set.html + pub struct Asn1Time; +} + +impl Asn1TimeRef { + /// Find difference between two times + /// + /// This corresponds to [`ASN1_TIME_diff`]. + /// + /// [`ASN1_TIME_diff`]: https://www.openssl.org/docs/man1.1.0/crypto/ASN1_TIME_diff.html + pub fn diff(&self, compare: &Self) -> Result { + let mut days = 0; + let mut secs = 0; + let other = compare.as_ptr(); + + let err = unsafe { ffi::ASN1_TIME_diff(&mut days, &mut secs, self.as_ptr(), other) }; + + match err { + 0 => Err(ErrorStack::get()), + _ => Ok(TimeDiff { days, secs }), + } + } + + /// Compare two times + /// + /// This corresponds to [`ASN1_TIME_compare`] but is implemented using [`diff`] so that it is + /// also supported on older versions of OpenSSL. + /// + /// [`ASN1_TIME_compare`]: https://www.openssl.org/docs/man1.1.1/man3/ASN1_TIME_compare.html + /// [`diff`]: struct.Asn1TimeRef.html#method.diff + pub fn compare(&self, other: &Self) -> Result { + let d = self.diff(other)?; + if d.days > 0 || d.secs > 0 { + return Ok(Ordering::Less); + } + if d.days < 0 || d.secs < 0 { + return Ok(Ordering::Greater); + } + + Ok(Ordering::Equal) + } +} + +impl PartialEq for Asn1TimeRef { + fn eq(&self, other: &Asn1TimeRef) -> bool { + self.diff(other) + .map(|t| t.days == 0 && t.secs == 0) + .unwrap_or(false) + } +} + +impl PartialEq for Asn1TimeRef { + fn eq(&self, other: &Asn1Time) -> bool { + self.diff(other) + .map(|t| t.days == 0 && t.secs == 0) + .unwrap_or(false) + } +} + +impl<'a> PartialEq for &'a Asn1TimeRef { + fn eq(&self, other: &Asn1Time) -> bool { + self.diff(other) + .map(|t| t.days == 0 && t.secs == 0) + .unwrap_or(false) + } +} + +impl PartialOrd for Asn1TimeRef { + fn partial_cmp(&self, other: &Asn1TimeRef) -> Option { + self.compare(other).ok() + } +} + +impl PartialOrd for Asn1TimeRef { + fn partial_cmp(&self, other: &Asn1Time) -> Option { + self.compare(other).ok() + } +} + +impl<'a> PartialOrd for &'a Asn1TimeRef { + fn partial_cmp(&self, other: &Asn1Time) -> Option { + self.compare(other).ok() + } +} + +impl fmt::Display for Asn1TimeRef { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + unsafe { + let mem_bio = match MemBio::new() { + Err(_) => return f.write_str("error"), + Ok(m) => m, + }; + let print_result = cvt(ffi::ASN1_TIME_print(mem_bio.as_ptr(), self.as_ptr())); + match print_result { + Err(_) => f.write_str("error"), + Ok(_) => f.write_str(str::from_utf8_unchecked(mem_bio.get_buf())), + } + } + } +} + +impl fmt::Debug for Asn1TimeRef { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.write_str(&self.to_string()) + } +} + +impl Asn1Time { + fn new() -> Result { + ffi::init(); + + unsafe { + let handle = cvt_p(ffi::ASN1_TIME_new())?; + Ok(Asn1Time::from_ptr(handle)) + } + } + + fn from_period(period: c_long) -> Result { + ffi::init(); + + unsafe { + let handle = cvt_p(ffi::X509_gmtime_adj(ptr::null_mut(), period))?; + Ok(Asn1Time::from_ptr(handle)) + } + } + + /// Creates a new time on specified interval in days from now + pub fn days_from_now(days: u32) -> Result { + Asn1Time::from_period(days as c_long * 60 * 60 * 24) + } + + /// Creates a new time from the specified `time_t` value + pub fn from_unix(time: time_t) -> Result { + ffi::init(); + + unsafe { + let handle = cvt_p(ffi::ASN1_TIME_set(ptr::null_mut(), time))?; + Ok(Asn1Time::from_ptr(handle)) + } + } + + /// Creates a new time corresponding to the specified ASN1 time string. + /// + /// This corresponds to [`ASN1_TIME_set_string`]. + /// + /// [`ASN1_TIME_set_string`]: https://www.openssl.org/docs/manmaster/man3/ASN1_TIME_set_string.html + #[allow(clippy::should_implement_trait)] + pub fn from_str(s: &str) -> Result { + unsafe { + let s = CString::new(s).unwrap(); + + let time = Asn1Time::new()?; + cvt(ffi::ASN1_TIME_set_string(time.as_ptr(), s.as_ptr()))?; + + Ok(time) + } + } +} + +impl PartialEq for Asn1Time { + fn eq(&self, other: &Asn1Time) -> bool { + self.diff(other) + .map(|t| t.days == 0 && t.secs == 0) + .unwrap_or(false) + } +} + +impl PartialEq for Asn1Time { + fn eq(&self, other: &Asn1TimeRef) -> bool { + self.diff(other) + .map(|t| t.days == 0 && t.secs == 0) + .unwrap_or(false) + } +} + +impl<'a> PartialEq<&'a Asn1TimeRef> for Asn1Time { + fn eq(&self, other: &&'a Asn1TimeRef) -> bool { + self.diff(other) + .map(|t| t.days == 0 && t.secs == 0) + .unwrap_or(false) + } +} + +impl PartialOrd for Asn1Time { + fn partial_cmp(&self, other: &Asn1Time) -> Option { + self.compare(other).ok() + } +} + +impl PartialOrd for Asn1Time { + fn partial_cmp(&self, other: &Asn1TimeRef) -> Option { + self.compare(other).ok() + } +} + +impl<'a> PartialOrd<&'a Asn1TimeRef> for Asn1Time { + fn partial_cmp(&self, other: &&'a Asn1TimeRef) -> Option { + self.compare(other).ok() + } +} + +foreign_type_and_impl_send_sync! { + type CType = ffi::ASN1_STRING; + fn drop = ffi::ASN1_STRING_free; + /// Primary ASN.1 type used by OpenSSL + /// + /// Almost all ASN.1 types in OpenSSL are represented by ASN1_STRING + /// structures. This implementation uses [ASN1_STRING-to_UTF8] to preserve + /// compatibility with Rust's String. + /// + /// [ASN1_STRING-to_UTF8]: https://www.openssl.org/docs/man1.1.0/crypto/ASN1_STRING_to_UTF8.html + pub struct Asn1String; +} + +impl Asn1StringRef { + /// Converts the ASN.1 underlying format to UTF8 + /// + /// ASN.1 strings may utilize UTF-16, ASCII, BMP, or UTF8. This is important to + /// consume the string in a meaningful way without knowing the underlying + /// format. + pub fn as_utf8(&self) -> Result { + unsafe { + let mut ptr = ptr::null_mut(); + let len = ffi::ASN1_STRING_to_UTF8(&mut ptr, self.as_ptr()); + if len < 0 { + return Err(ErrorStack::get()); + } + + Ok(OpensslString::from_ptr(ptr as *mut c_char)) + } + } + + /// Return the string as an array of bytes. + /// + /// The bytes do not directly correspond to UTF-8 encoding. To interact with + /// strings in rust, it is preferable to use [`as_utf8`] + /// + /// [`as_utf8`]: struct.Asn1String.html#method.as_utf8 + pub fn as_slice(&self) -> &[u8] { + unsafe { slice::from_raw_parts(ASN1_STRING_get0_data(self.as_ptr()), self.len()) } + } + + /// Returns the number of bytes in the string. + pub fn len(&self) -> usize { + unsafe { ffi::ASN1_STRING_length(self.as_ptr()) as usize } + } + + /// Determines if the string is empty. + pub fn is_empty(&self) -> bool { + self.len() == 0 + } +} + +impl fmt::Debug for Asn1StringRef { + fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { + match self.as_utf8() { + Ok(openssl_string) => openssl_string.fmt(fmt), + Err(_) => fmt.write_str("error"), + } + } +} + +foreign_type_and_impl_send_sync! { + type CType = ffi::ASN1_INTEGER; + fn drop = ffi::ASN1_INTEGER_free; + + /// Numeric representation + /// + /// Integers in ASN.1 may include BigNum, int64 or uint64. BigNum implementation + /// can be found within [`bn`] module. + /// + /// OpenSSL documentation includes [`ASN1_INTEGER_set`]. + /// + /// [`bn`]: ../bn/index.html + /// [`ASN1_INTEGER_set`]: https://www.openssl.org/docs/man1.1.0/crypto/ASN1_INTEGER_set.html + pub struct Asn1Integer; +} + +impl Asn1Integer { + /// Converts a bignum to an `Asn1Integer`. + /// + /// Corresponds to [`BN_to_ASN1_INTEGER`]. Also see + /// [`BigNumRef::to_asn1_integer`]. + /// + /// [`BN_to_ASN1_INTEGER`]: https://www.openssl.org/docs/man1.1.0/crypto/BN_to_ASN1_INTEGER.html + /// [`BigNumRef::to_asn1_integer`]: ../bn/struct.BigNumRef.html#method.to_asn1_integer + pub fn from_bn(bn: &BigNumRef) -> Result { + bn.to_asn1_integer() + } +} + +impl Asn1IntegerRef { + #[allow(clippy::unnecessary_cast)] + #[allow(missing_docs)] + #[deprecated(since = "0.10.6", note = "use to_bn instead")] + pub fn get(&self) -> i64 { + unsafe { crate::ffi::ASN1_INTEGER_get(self.as_ptr()) as i64 } + } + + /// Converts the integer to a `BigNum`. + /// + /// This corresponds to [`ASN1_INTEGER_to_BN`]. + /// + /// [`ASN1_INTEGER_to_BN`]: https://www.openssl.org/docs/man1.1.0/crypto/ASN1_INTEGER_get.html + pub fn to_bn(&self) -> Result { + unsafe { + cvt_p(crate::ffi::ASN1_INTEGER_to_BN( + self.as_ptr(), + ptr::null_mut(), + )) + .map(|p| BigNum::from_ptr(p)) + } + } + + /// Sets the ASN.1 value to the value of a signed 32-bit integer, for larger numbers + /// see [`bn`]. + /// + /// OpenSSL documentation at [`ASN1_INTEGER_set`] + /// + /// [`bn`]: ../bn/struct.BigNumRef.html#method.to_asn1_integer + /// [`ASN1_INTEGER_set`]: https://www.openssl.org/docs/man1.1.0/crypto/ASN1_INTEGER_set.html + pub fn set(&mut self, value: i32) -> Result<(), ErrorStack> { + unsafe { cvt(crate::ffi::ASN1_INTEGER_set(self.as_ptr(), value as c_long)).map(|_| ()) } + } +} + +foreign_type_and_impl_send_sync! { + type CType = ffi::ASN1_BIT_STRING; + fn drop = ffi::ASN1_BIT_STRING_free; + /// Sequence of bytes + /// + /// Asn1BitString is used in [`x509`] certificates for the signature. + /// The bit string acts as a collection of bytes. + /// + /// [`x509`]: ../x509/struct.X509.html#method.signature + pub struct Asn1BitString; +} + +impl Asn1BitStringRef { + /// Returns the Asn1BitString as a slice. + pub fn as_slice(&self) -> &[u8] { + unsafe { slice::from_raw_parts(ASN1_STRING_get0_data(self.as_ptr() as *mut _), self.len()) } + } + + /// Returns the number of bytes in the string. + pub fn len(&self) -> usize { + unsafe { ffi::ASN1_STRING_length(self.as_ptr() as *const _) as usize } + } + + /// Determines if the string is empty. + pub fn is_empty(&self) -> bool { + self.len() == 0 + } +} + +foreign_type_and_impl_send_sync! { + type CType = ffi::ASN1_OBJECT; + fn drop = ffi::ASN1_OBJECT_free; + + /// Object Identifier + /// + /// Represents an ASN.1 Object. Typically, NIDs, or numeric identifiers + /// are stored as a table within the [`Nid`] module. These constants are + /// used to determine attributes of a certificate, such as mapping the + /// attribute "CommonName" to "CN" which is represented as the OID of 13. + /// This attribute is a constant in the [`nid::COMMONNAME`]. + /// + /// OpenSSL documentation at [`OBJ_nid2obj`] + /// + /// [`Nid`]: ../nid/index.html + /// [`nid::COMMONNAME`]: ../nid/constant.COMMONNAME.html + /// [`OBJ_nid2obj`]: https://www.openssl.org/docs/man1.1.0/crypto/OBJ_obj2nid.html + pub struct Asn1Object; +} + +impl Stackable for Asn1Object { + type StackType = ffi::stack_st_ASN1_OBJECT; +} + +impl Asn1Object { + /// Constructs an ASN.1 Object Identifier from a string representation of + /// the OID. + /// + /// This corresponds to [`OBJ_txt2obj`]. + /// + /// [`OBJ_txt2obj`]: https://www.openssl.org/docs/man1.1.0/man3/OBJ_txt2obj.html + #[allow(clippy::should_implement_trait)] + pub fn from_str(txt: &str) -> Result { + unsafe { + ffi::init(); + let txt = CString::new(txt).unwrap(); + let obj: *mut ffi::ASN1_OBJECT = cvt_p(ffi::OBJ_txt2obj(txt.as_ptr() as *const _, 0))?; + Ok(Asn1Object::from_ptr(obj)) + } + } +} + +impl Asn1ObjectRef { + /// Returns the NID associated with this OID. + pub fn nid(&self) -> Nid { + unsafe { Nid::from_raw(ffi::OBJ_obj2nid(self.as_ptr())) } + } +} + +impl fmt::Display for Asn1ObjectRef { + fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { + unsafe { + let mut buf = [0; 80]; + let len = ffi::OBJ_obj2txt( + buf.as_mut_ptr() as *mut _, + buf.len() as c_int, + self.as_ptr(), + 0, + ); + match str::from_utf8(&buf[..len as usize]) { + Err(_) => fmt.write_str("error"), + Ok(s) => fmt.write_str(s), + } + } + } +} + +impl fmt::Debug for Asn1ObjectRef { + fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { + fmt.write_str(self.to_string().as_str()) + } +} + +use crate::ffi::ASN1_STRING_get0_data; + +#[cfg(test)] +mod tests { + use super::*; + + use crate::bn::BigNum; + use crate::nid::Nid; + + /// Tests conversion between BigNum and Asn1Integer. + #[test] + fn bn_cvt() { + fn roundtrip(bn: BigNum) { + let large = Asn1Integer::from_bn(&bn).unwrap(); + assert_eq!(large.to_bn().unwrap(), bn); + } + + roundtrip(BigNum::from_dec_str("1000000000000000000000000000000000").unwrap()); + roundtrip(-BigNum::from_dec_str("1000000000000000000000000000000000").unwrap()); + roundtrip(BigNum::from_u32(1234).unwrap()); + roundtrip(-BigNum::from_u32(1234).unwrap()); + } + + #[test] + fn time_from_str() { + Asn1Time::from_str("99991231235959Z").unwrap(); + } + + #[test] + fn time_from_unix() { + let t = Asn1Time::from_unix(0).unwrap(); + assert_eq!("Jan 1 00:00:00 1970 GMT", t.to_string()); + } + + #[test] + fn time_eq() { + let a = Asn1Time::from_str("99991231235959Z").unwrap(); + let b = Asn1Time::from_str("99991231235959Z").unwrap(); + let c = Asn1Time::from_str("99991231235958Z").unwrap(); + let a_ref = a.as_ref(); + let b_ref = b.as_ref(); + let c_ref = c.as_ref(); + assert!(a == b); + assert!(a != c); + assert!(a == b_ref); + assert!(a != c_ref); + assert!(b_ref == a); + assert!(c_ref != a); + assert!(a_ref == b_ref); + assert!(a_ref != c_ref); + } + + #[test] + fn time_ord() { + let a = Asn1Time::from_str("99991231235959Z").unwrap(); + let b = Asn1Time::from_str("99991231235959Z").unwrap(); + let c = Asn1Time::from_str("99991231235958Z").unwrap(); + let a_ref = a.as_ref(); + let b_ref = b.as_ref(); + let c_ref = c.as_ref(); + assert!(a >= b); + assert!(a > c); + assert!(b <= a); + assert!(c < a); + + assert!(a_ref >= b); + assert!(a_ref > c); + assert!(b_ref <= a); + assert!(c_ref < a); + + assert!(a >= b_ref); + assert!(a > c_ref); + assert!(b <= a_ref); + assert!(c < a_ref); + + assert!(a_ref >= b_ref); + assert!(a_ref > c_ref); + assert!(b_ref <= a_ref); + assert!(c_ref < a_ref); + } + + #[test] + fn object_from_str() { + let object = Asn1Object::from_str("2.16.840.1.101.3.4.2.1").unwrap(); + assert_eq!(object.nid(), Nid::SHA256); + } + + #[test] + fn object_from_str_with_invalid_input() { + Asn1Object::from_str("NOT AN OID") + .map(|object| object.to_string()) + .expect_err("parsing invalid OID should fail"); + } +} diff --git a/crates/boring/src/base64.rs b/crates/boring/src/base64.rs new file mode 100644 index 000000000..2e9ef17d3 --- /dev/null +++ b/crates/boring/src/base64.rs @@ -0,0 +1,134 @@ +//! Base64 encoding support. +use crate::cvt_n; +use crate::error::ErrorStack; +use crate::ffi; +use libc::c_int; + +/// Encodes a slice of bytes to a base64 string. +/// +/// This corresponds to [`EVP_EncodeBlock`]. +/// +/// # Panics +/// +/// Panics if the input length or computed output length overflow a signed C integer. +/// +/// [`EVP_EncodeBlock`]: https://www.openssl.org/docs/man1.1.1/man3/EVP_DecodeBlock.html +pub fn encode_block(src: &[u8]) -> String { + assert!(src.len() <= c_int::max_value() as usize); + let src_len = src.len(); + + let len = encoded_len(src_len).unwrap(); + let mut out = Vec::with_capacity(len); + + // SAFETY: `encoded_len` ensures space for 4 output characters + // for every 3 input bytes including padding and nul terminator. + // `EVP_EncodeBlock` will write only single byte ASCII characters. + // `EVP_EncodeBlock` will only write to not read from `out`. + unsafe { + let out_len = ffi::EVP_EncodeBlock(out.as_mut_ptr(), src.as_ptr(), src_len); + out.set_len(out_len); + String::from_utf8_unchecked(out) + } +} + +/// Decodes a base64-encoded string to bytes. +/// +/// This corresponds to [`EVP_DecodeBlock`]. +/// +/// # Panics +/// +/// Panics if the input length or computed output length overflow a signed C integer. +/// +/// [`EVP_DecodeBlock`]: https://www.openssl.org/docs/man1.1.1/man3/EVP_DecodeBlock.html +pub fn decode_block(src: &str) -> Result, ErrorStack> { + let src = src.trim(); + + // https://github.com/openssl/openssl/issues/12143 + if src.is_empty() { + return Ok(vec![]); + } + + assert!(src.len() <= c_int::max_value() as usize); + let src_len = src.len(); + + let len = decoded_len(src_len).unwrap(); + let mut out = Vec::with_capacity(len); + + // SAFETY: `decoded_len` ensures space for 3 output bytes + // for every 4 input characters including padding. + // `EVP_DecodeBlock` can write fewer bytes after stripping + // leading and trailing whitespace, but never more. + // `EVP_DecodeBlock` will only write to not read from `out`. + unsafe { + let out_len = cvt_n(ffi::EVP_DecodeBlock( + out.as_mut_ptr(), + src.as_ptr(), + src_len, + ))?; + out.set_len(out_len as usize); + } + + if src.ends_with('=') { + out.pop(); + if src.ends_with("==") { + out.pop(); + } + } + + Ok(out) +} + +fn encoded_len(src_len: usize) -> Option { + let mut len = (src_len / 3).checked_mul(4)?; + + if src_len % 3 != 0 { + len = len.checked_add(4)?; + } + + len = len.checked_add(1)?; + + Some(len) +} + +fn decoded_len(src_len: usize) -> Option { + let mut len = (src_len / 4).checked_mul(3)?; + + if src_len % 4 != 0 { + len = len.checked_add(3)?; + } + + Some(len) +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_encode_block() { + assert_eq!("".to_string(), encode_block(b"")); + assert_eq!("Zg==".to_string(), encode_block(b"f")); + assert_eq!("Zm8=".to_string(), encode_block(b"fo")); + assert_eq!("Zm9v".to_string(), encode_block(b"foo")); + assert_eq!("Zm9vYg==".to_string(), encode_block(b"foob")); + assert_eq!("Zm9vYmE=".to_string(), encode_block(b"fooba")); + assert_eq!("Zm9vYmFy".to_string(), encode_block(b"foobar")); + } + + #[test] + fn test_decode_block() { + assert_eq!(b"".to_vec(), decode_block("").unwrap()); + assert_eq!(b"f".to_vec(), decode_block("Zg==").unwrap()); + assert_eq!(b"fo".to_vec(), decode_block("Zm8=").unwrap()); + assert_eq!(b"foo".to_vec(), decode_block("Zm9v").unwrap()); + assert_eq!(b"foob".to_vec(), decode_block("Zm9vYg==").unwrap()); + assert_eq!(b"fooba".to_vec(), decode_block("Zm9vYmE=").unwrap()); + assert_eq!(b"foobar".to_vec(), decode_block("Zm9vYmFy").unwrap()); + } + + #[test] + fn test_strip_whitespace() { + assert_eq!(b"foobar".to_vec(), decode_block(" Zm9vYmFy\n").unwrap()); + assert_eq!(b"foob".to_vec(), decode_block(" Zm9vYg==\n").unwrap()); + } +} diff --git a/crates/boring/src/bio.rs b/crates/boring/src/bio.rs new file mode 100644 index 000000000..adb119b87 --- /dev/null +++ b/crates/boring/src/bio.rs @@ -0,0 +1,74 @@ +use crate::ffi; +use crate::ffi::BIO_new_mem_buf; +use std::marker::PhantomData; +use std::ptr; +use std::slice; + +use crate::cvt_p; +use crate::error::ErrorStack; + +pub struct MemBioSlice<'a>(*mut ffi::BIO, PhantomData<&'a [u8]>); + +impl<'a> Drop for MemBioSlice<'a> { + fn drop(&mut self) { + unsafe { + ffi::BIO_free_all(self.0); + } + } +} + +impl<'a> MemBioSlice<'a> { + pub fn new(buf: &'a [u8]) -> Result, ErrorStack> { + #[cfg(not(any(feature = "fips", feature = "fips-link-precompiled")))] + type BufLen = isize; + #[cfg(any(feature = "fips", feature = "fips-link-precompiled"))] + type BufLen = libc::c_int; + + ffi::init(); + + assert!(buf.len() <= BufLen::max_value() as usize); + let bio = unsafe { + cvt_p(BIO_new_mem_buf( + buf.as_ptr() as *const _, + buf.len() as BufLen, + ))? + }; + + Ok(MemBioSlice(bio, PhantomData)) + } + + pub fn as_ptr(&self) -> *mut ffi::BIO { + self.0 + } +} + +pub struct MemBio(*mut ffi::BIO); + +impl Drop for MemBio { + fn drop(&mut self) { + unsafe { + ffi::BIO_free_all(self.0); + } + } +} + +impl MemBio { + pub fn new() -> Result { + ffi::init(); + + let bio = unsafe { cvt_p(ffi::BIO_new(ffi::BIO_s_mem()))? }; + Ok(MemBio(bio)) + } + + pub fn as_ptr(&self) -> *mut ffi::BIO { + self.0 + } + + pub fn get_buf(&self) -> &[u8] { + unsafe { + let mut ptr = ptr::null_mut(); + let len = ffi::BIO_get_mem_data(self.0, &mut ptr); + slice::from_raw_parts(ptr as *const _ as *const _, len as usize) + } + } +} diff --git a/crates/boring/src/bn.rs b/crates/boring/src/bn.rs new file mode 100644 index 000000000..6ad257825 --- /dev/null +++ b/crates/boring/src/bn.rs @@ -0,0 +1,1308 @@ +//! BigNum implementation +//! +//! Large numbers are important for a cryptographic library. OpenSSL implementation +//! of BigNum uses dynamically assigned memory to store an array of bit chunks. This +//! allows numbers of any size to be compared and mathematical functions performed. +//! +//! OpenSSL wiki describes the [`BIGNUM`] data structure. +//! +//! # Examples +//! +//! ``` +//! use boring::bn::BigNum; +//! use boring::error::ErrorStack; +//! +//! fn main() -> Result<(), ErrorStack> { +//! let a = BigNum::new()?; // a = 0 +//! let b = BigNum::from_dec_str("1234567890123456789012345")?; +//! let c = &a * &b; +//! assert_eq!(a, c); +//! Ok(()) +//! } +//! ``` +//! +//! [`BIGNUM`]: https://wiki.openssl.org/index.php/Manual:Bn_internal(3) +use crate::ffi; +use foreign_types::{ForeignType, ForeignTypeRef}; +use libc::{c_int, size_t}; +use std::cmp::Ordering; +use std::ffi::CString; +use std::ops::{Add, Deref, Div, Mul, Neg, Rem, Shl, Shr, Sub}; +use std::{fmt, ptr}; + +use crate::asn1::Asn1Integer; +use crate::error::ErrorStack; +use crate::ffi::BN_is_negative; +use crate::string::OpensslString; +use crate::{cvt, cvt_n, cvt_p}; + +/// Options for the most significant bits of a randomly generated `BigNum`. +pub struct MsbOption(c_int); + +impl MsbOption { + /// The most significant bit of the number may be 0. + pub const MAYBE_ZERO: MsbOption = MsbOption(-1); + + /// The most significant bit of the number must be 1. + pub const ONE: MsbOption = MsbOption(0); + + /// The most significant two bits of the number must be 1. + /// + /// The number of bits in the product of two such numbers will always be exactly twice the + /// number of bits in the original numbers. + pub const TWO_ONES: MsbOption = MsbOption(1); +} + +foreign_type_and_impl_send_sync! { + type CType = ffi::BN_CTX; + fn drop = ffi::BN_CTX_free; + + /// Temporary storage for BigNums on the secure heap + /// + /// BigNum values are stored dynamically and therefore can be expensive + /// to allocate. BigNumContext and the OpenSSL [`BN_CTX`] structure are used + /// internally when passing BigNum values between subroutines. + /// + /// [`BN_CTX`]: https://www.openssl.org/docs/man1.1.0/crypto/BN_CTX_new.html + pub struct BigNumContext; +} + +impl BigNumContext { + /// Returns a new `BigNumContext`. + /// + /// See OpenSSL documentation at [`BN_CTX_new`]. + /// + /// [`BN_CTX_new`]: https://www.openssl.org/docs/man1.1.0/crypto/BN_CTX_new.html + pub fn new() -> Result { + unsafe { + ffi::init(); + cvt_p(ffi::BN_CTX_new()).map(|p| BigNumContext::from_ptr(p)) + } + } +} + +foreign_type_and_impl_send_sync! { + type CType = ffi::BIGNUM; + fn drop = ffi::BN_free; + + /// Dynamically sized large number impelementation + /// + /// Perform large number mathematics. Create a new BigNum + /// with [`new`]. Perform standard mathematics on large numbers using + /// methods from [`Dref`] + /// + /// OpenSSL documenation at [`BN_new`]. + /// + /// [`new`]: struct.BigNum.html#method.new + /// [`Dref`]: struct.BigNum.html#deref-methods + /// [`BN_new`]: https://www.openssl.org/docs/man1.1.0/crypto/BN_new.html + /// + /// # Examples + /// ``` + /// use boring::bn::BigNum; + /// # use boring::error::ErrorStack; + /// # fn bignums() -> Result< (), ErrorStack > { + /// let little_big = BigNum::from_u32(std::u32::MAX)?; + /// assert_eq!(*&little_big.num_bytes(), 4); + /// # Ok(()) + /// # } + /// # fn main () { bignums(); } + /// ``` + pub struct BigNum; +} + +impl BigNumRef { + /// Erases the memory used by this `BigNum`, resetting its value to 0. + /// + /// This can be used to destroy sensitive data such as keys when they are no longer needed. + /// + /// OpenSSL documentation at [`BN_clear`] + /// + /// [`BN_clear`]: https://www.openssl.org/docs/man1.1.0/crypto/BN_clear.html + pub fn clear(&mut self) { + unsafe { ffi::BN_clear(self.as_ptr()) } + } + + /// Adds a `u32` to `self`. + /// + /// OpenSSL documentation at [`BN_add_word`] + /// + /// [`BN_add_word`]: https://www.openssl.org/docs/man1.1.0/crypto/BN_add_word.html + pub fn add_word(&mut self, w: u32) -> Result<(), ErrorStack> { + unsafe { cvt(ffi::BN_add_word(self.as_ptr(), w as ffi::BN_ULONG)).map(|_| ()) } + } + + /// Subtracts a `u32` from `self`. + /// + /// OpenSSL documentation at [`BN_sub_word`] + /// + /// [`BN_sub_word`]: https://www.openssl.org/docs/man1.1.0/crypto/BN_sub_word.html + pub fn sub_word(&mut self, w: u32) -> Result<(), ErrorStack> { + unsafe { cvt(ffi::BN_sub_word(self.as_ptr(), w as ffi::BN_ULONG)).map(|_| ()) } + } + + /// Multiplies a `u32` by `self`. + /// + /// OpenSSL documentation at [`BN_mul_word`] + /// + /// [`BN_mul_word`]: https://www.openssl.org/docs/man1.1.0/crypto/BN_mul_word.html + pub fn mul_word(&mut self, w: u32) -> Result<(), ErrorStack> { + unsafe { cvt(ffi::BN_mul_word(self.as_ptr(), w as ffi::BN_ULONG)).map(|_| ()) } + } + + /// Divides `self` by a `u32`, returning the remainder. + /// + /// OpenSSL documentation at [`BN_div_word`] + /// + /// [`BN_div_word`]: https://www.openssl.org/docs/man1.1.0/crypto/BN_div_word.html + #[allow(clippy::useless_conversion)] + pub fn div_word(&mut self, w: u32) -> Result { + unsafe { + let r = ffi::BN_div_word(self.as_ptr(), w.into()); + if r == ffi::BN_ULONG::max_value() { + Err(ErrorStack::get()) + } else { + Ok(r.into()) + } + } + } + + /// Returns the result of `self` modulo `w`. + /// + /// OpenSSL documentation at [`BN_mod_word`] + /// + /// [`BN_mod_word`]: https://www.openssl.org/docs/man1.1.0/crypto/BN_mod_word.html + #[allow(clippy::useless_conversion)] + pub fn mod_word(&self, w: u32) -> Result { + unsafe { + let r = ffi::BN_mod_word(self.as_ptr(), w.into()); + if r == ffi::BN_ULONG::max_value() { + Err(ErrorStack::get()) + } else { + Ok(r.into()) + } + } + } + + /// Places a cryptographically-secure pseudo-random nonnegative + /// number less than `self` in `rnd`. + /// + /// OpenSSL documentation at [`BN_rand_range`] + /// + /// [`BN_rand_range`]: https://www.openssl.org/docs/man1.1.0/crypto/BN_rand_range.html + pub fn rand_range(&self, rnd: &mut BigNumRef) -> Result<(), ErrorStack> { + unsafe { cvt(ffi::BN_rand_range(rnd.as_ptr(), self.as_ptr())).map(|_| ()) } + } + + /// The cryptographically weak counterpart to `rand_in_range`. + /// + /// OpenSSL documentation at [`BN_pseudo_rand_range`] + /// + /// [`BN_pseudo_rand_range`]: https://www.openssl.org/docs/man1.1.0/crypto/BN_pseudo_rand_range.html + pub fn pseudo_rand_range(&self, rnd: &mut BigNumRef) -> Result<(), ErrorStack> { + unsafe { cvt(ffi::BN_pseudo_rand_range(rnd.as_ptr(), self.as_ptr())).map(|_| ()) } + } + + /// Sets bit `n`. Equivalent to `self |= (1 << n)`. + /// + /// When setting a bit outside of `self`, it is expanded. + /// + /// OpenSSL documentation at [`BN_set_bit`] + /// + /// [`BN_set_bit`]: https://www.openssl.org/docs/man1.1.0/crypto/BN_set_bit.html + #[allow(clippy::useless_conversion)] + pub fn set_bit(&mut self, n: i32) -> Result<(), ErrorStack> { + unsafe { cvt(ffi::BN_set_bit(self.as_ptr(), n.into())).map(|_| ()) } + } + + /// Clears bit `n`, setting it to 0. Equivalent to `self &= ~(1 << n)`. + /// + /// When clearing a bit outside of `self`, an error is returned. + /// + /// OpenSSL documentation at [`BN_clear_bit`] + /// + /// [`BN_clear_bit`]: https://www.openssl.org/docs/man1.1.0/crypto/BN_clear_bit.html + #[allow(clippy::useless_conversion)] + pub fn clear_bit(&mut self, n: i32) -> Result<(), ErrorStack> { + unsafe { cvt(ffi::BN_clear_bit(self.as_ptr(), n.into())).map(|_| ()) } + } + + /// Returns `true` if the `n`th bit of `self` is set to 1, `false` otherwise. + /// + /// OpenSSL documentation at [`BN_is_bit_set`] + /// + /// [`BN_is_bit_set`]: https://www.openssl.org/docs/man1.1.0/crypto/BN_is_bit_set.html + #[allow(clippy::useless_conversion)] + pub fn is_bit_set(&self, n: i32) -> bool { + unsafe { ffi::BN_is_bit_set(self.as_ptr(), n.into()) == 1 } + } + + /// Truncates `self` to the lowest `n` bits. + /// + /// An error occurs if `self` is already shorter than `n` bits. + /// + /// OpenSSL documentation at [`BN_mask_bits`] + /// + /// [`BN_mask_bits`]: https://www.openssl.org/docs/man1.1.0/crypto/BN_mask_bits.html + #[allow(clippy::useless_conversion)] + pub fn mask_bits(&mut self, n: i32) -> Result<(), ErrorStack> { + unsafe { cvt(ffi::BN_mask_bits(self.as_ptr(), n.into())).map(|_| ()) } + } + + /// Places `a << 1` in `self`. Equivalent to `self * 2`. + /// + /// OpenSSL documentation at [`BN_lshift1`] + /// + /// [`BN_lshift1`]: https://www.openssl.org/docs/man1.1.0/crypto/BN_lshift1.html + pub fn lshift1(&mut self, a: &BigNumRef) -> Result<(), ErrorStack> { + unsafe { cvt(ffi::BN_lshift1(self.as_ptr(), a.as_ptr())).map(|_| ()) } + } + + /// Places `a >> 1` in `self`. Equivalent to `self / 2`. + /// + /// OpenSSL documentation at [`BN_rshift1`] + /// + /// [`BN_rshift1`]: https://www.openssl.org/docs/man1.1.0/crypto/BN_rshift1.html + pub fn rshift1(&mut self, a: &BigNumRef) -> Result<(), ErrorStack> { + unsafe { cvt(ffi::BN_rshift1(self.as_ptr(), a.as_ptr())).map(|_| ()) } + } + + /// Places `a + b` in `self`. [`core::ops::Add`] is also implemented for `BigNumRef`. + /// + /// OpenSSL documentation at [`BN_add`] + /// + /// [`core::ops::Add`]: struct.BigNumRef.html#method.add + /// [`BN_add`]: https://www.openssl.org/docs/man1.1.0/crypto/BN_add.html + pub fn checked_add(&mut self, a: &BigNumRef, b: &BigNumRef) -> Result<(), ErrorStack> { + unsafe { cvt(ffi::BN_add(self.as_ptr(), a.as_ptr(), b.as_ptr())).map(|_| ()) } + } + + /// Places `a - b` in `self`. [`core::ops::Sub`] is also implemented for `BigNumRef`. + /// + /// OpenSSL documentation at [`BN_sub`] + /// + /// [`core::ops::Sub`]: struct.BigNumRef.html#method.sub + /// [`BN_sub`]: https://www.openssl.org/docs/man1.1.0/crypto/BN_sub.html + pub fn checked_sub(&mut self, a: &BigNumRef, b: &BigNumRef) -> Result<(), ErrorStack> { + unsafe { cvt(ffi::BN_sub(self.as_ptr(), a.as_ptr(), b.as_ptr())).map(|_| ()) } + } + + /// Places `a << n` in `self`. Equivalent to `a * 2 ^ n`. + /// + /// OpenSSL documentation at [`BN_lshift`] + /// + /// [`BN_lshift`]: https://www.openssl.org/docs/man1.1.0/crypto/BN_lshift.html + #[allow(clippy::useless_conversion)] + pub fn lshift(&mut self, a: &BigNumRef, n: i32) -> Result<(), ErrorStack> { + unsafe { cvt(ffi::BN_lshift(self.as_ptr(), a.as_ptr(), n.into())).map(|_| ()) } + } + + /// Places `a >> n` in `self`. Equivalent to `a / 2 ^ n`. + /// + /// OpenSSL documentation at [`BN_rshift`] + /// + /// [`BN_rshift`]: https://www.openssl.org/docs/man1.1.0/crypto/BN_rshift.html + #[allow(clippy::useless_conversion)] + pub fn rshift(&mut self, a: &BigNumRef, n: i32) -> Result<(), ErrorStack> { + unsafe { cvt(ffi::BN_rshift(self.as_ptr(), a.as_ptr(), n.into())).map(|_| ()) } + } + + /// Creates a new BigNum with the same value. + /// + /// OpenSSL documentation at [`BN_dup`] + /// + /// [`BN_dup`]: https://www.openssl.org/docs/man1.1.0/crypto/BN_dup.html + pub fn to_owned(&self) -> Result { + unsafe { cvt_p(ffi::BN_dup(self.as_ptr())).map(|b| BigNum::from_ptr(b)) } + } + + /// Sets the sign of `self`. Pass true to set `self` to a negative. False sets + /// `self` positive. + pub fn set_negative(&mut self, negative: bool) { + unsafe { ffi::BN_set_negative(self.as_ptr(), negative as c_int) } + } + + /// Compare the absolute values of `self` and `oth`. + /// + /// OpenSSL documentation at [`BN_ucmp`] + /// + /// [`BN_ucmp`]: https://www.openssl.org/docs/man1.1.0/crypto/BN_ucmp.html + /// + /// # Examples + /// + /// ``` + /// # use boring::bn::BigNum; + /// # use std::cmp::Ordering; + /// let s = -BigNum::from_u32(8).unwrap(); + /// let o = BigNum::from_u32(8).unwrap(); + /// + /// assert_eq!(s.ucmp(&o), Ordering::Equal); + /// ``` + pub fn ucmp(&self, oth: &BigNumRef) -> Ordering { + unsafe { ffi::BN_ucmp(self.as_ptr(), oth.as_ptr()).cmp(&0) } + } + + /// Returns `true` if `self` is negative. + pub fn is_negative(&self) -> bool { + unsafe { BN_is_negative(self.as_ptr()) == 1 } + } + + /// Returns the number of significant bits in `self`. + /// + /// OpenSSL documentation at [`BN_num_bits`] + /// + /// [`BN_num_bits`]: https://www.openssl.org/docs/man1.1.0/crypto/BN_num_bits.html + pub fn num_bits(&self) -> i32 { + unsafe { ffi::BN_num_bits(self.as_ptr()) as i32 } + } + + /// Returns the size of `self` in bytes. Implemented natively. + pub fn num_bytes(&self) -> i32 { + (self.num_bits() + 7) / 8 + } + + /// Generates a cryptographically strong pseudo-random `BigNum`, placing it in `self`. + /// + /// # Parameters + /// + /// * `bits`: Length of the number in bits. + /// * `msb`: The desired properties of the most significant bit. See [`constants`]. + /// * `odd`: If `true`, the generated number will be odd. + /// + /// # Examples + /// + /// ``` + /// use boring::bn::{BigNum, MsbOption}; + /// use boring::error::ErrorStack; + /// + /// fn generate_random() -> Result< BigNum, ErrorStack > { + /// let mut big = BigNum::new()?; + /// + /// // Generates a 128-bit odd random number + /// big.rand(128, MsbOption::MAYBE_ZERO, true); + /// Ok((big)) + /// } + /// ``` + /// + /// OpenSSL documentation at [`BN_rand`] + /// + /// [`constants`]: index.html#constants + /// [`BN_rand`]: https://www.openssl.org/docs/man1.1.0/crypto/BN_rand.html + #[allow(clippy::useless_conversion)] + pub fn rand(&mut self, bits: i32, msb: MsbOption, odd: bool) -> Result<(), ErrorStack> { + unsafe { + cvt(ffi::BN_rand( + self.as_ptr(), + bits.into(), + msb.0, + odd as c_int, + )) + .map(|_| ()) + } + } + + /// The cryptographically weak counterpart to `rand`. Not suitable for key generation. + /// + /// OpenSSL documentation at [`BN_pseudo_rand`] + /// + /// [`BN_pseudo_rand`]: https://www.openssl.org/docs/man1.1.0/crypto/BN_pseudo_rand.html + #[allow(clippy::useless_conversion)] + pub fn pseudo_rand(&mut self, bits: i32, msb: MsbOption, odd: bool) -> Result<(), ErrorStack> { + unsafe { + cvt(ffi::BN_pseudo_rand( + self.as_ptr(), + bits.into(), + msb.0, + odd as c_int, + )) + .map(|_| ()) + } + } + + /// Generates a prime number, placing it in `self`. + /// + /// # Parameters + /// + /// * `bits`: The length of the prime in bits (lower bound). + /// * `safe`: If true, returns a "safe" prime `p` so that `(p-1)/2` is also prime. + /// * `add`/`rem`: If `add` is set to `Some(add)`, `p % add == rem` will hold, where `p` is the + /// generated prime and `rem` is `1` if not specified (`None`). + /// + /// # Examples + /// + /// ``` + /// use boring::bn::BigNum; + /// use boring::error::ErrorStack; + /// + /// fn generate_weak_prime() -> Result< BigNum, ErrorStack > { + /// let mut big = BigNum::new()?; + /// + /// // Generates a 128-bit simple prime number + /// big.generate_prime(128, false, None, None); + /// Ok((big)) + /// } + /// ``` + /// + /// OpenSSL documentation at [`BN_generate_prime_ex`] + /// + /// [`BN_generate_prime_ex`]: https://www.openssl.org/docs/man1.1.0/crypto/BN_generate_prime_ex.html + pub fn generate_prime( + &mut self, + bits: i32, + safe: bool, + add: Option<&BigNumRef>, + rem: Option<&BigNumRef>, + ) -> Result<(), ErrorStack> { + unsafe { + cvt(ffi::BN_generate_prime_ex( + self.as_ptr(), + bits as c_int, + safe as c_int, + add.map(|n| n.as_ptr()).unwrap_or(ptr::null_mut()), + rem.map(|n| n.as_ptr()).unwrap_or(ptr::null_mut()), + ptr::null_mut(), + )) + .map(|_| ()) + } + } + + /// Places the result of `a * b` in `self`. + /// [`core::ops::Mul`] is also implemented for `BigNumRef`. + /// + /// OpenSSL documentation at [`BN_mul`] + /// + /// [`core::ops::Mul`]: struct.BigNumRef.html#method.mul + /// [`BN_mul`]: https://www.openssl.org/docs/man1.1.0/crypto/BN_mul.html + pub fn checked_mul( + &mut self, + a: &BigNumRef, + b: &BigNumRef, + ctx: &mut BigNumContextRef, + ) -> Result<(), ErrorStack> { + unsafe { + cvt(ffi::BN_mul( + self.as_ptr(), + a.as_ptr(), + b.as_ptr(), + ctx.as_ptr(), + )) + .map(|_| ()) + } + } + + /// Places the result of `a / b` in `self`. The remainder is discarded. + /// [`core::ops::Div`] is also implemented for `BigNumRef`. + /// + /// OpenSSL documentation at [`BN_div`] + /// + /// [`core::ops::Div`]: struct.BigNumRef.html#method.div + /// [`BN_div`]: https://www.openssl.org/docs/man1.1.0/crypto/BN_div.html + pub fn checked_div( + &mut self, + a: &BigNumRef, + b: &BigNumRef, + ctx: &mut BigNumContextRef, + ) -> Result<(), ErrorStack> { + unsafe { + cvt(ffi::BN_div( + self.as_ptr(), + ptr::null_mut(), + a.as_ptr(), + b.as_ptr(), + ctx.as_ptr(), + )) + .map(|_| ()) + } + } + + /// Places the result of `a % b` in `self`. + /// + /// OpenSSL documentation at [`BN_div`] + /// + /// [`BN_div`]: https://www.openssl.org/docs/man1.1.0/crypto/BN_div.html + pub fn checked_rem( + &mut self, + a: &BigNumRef, + b: &BigNumRef, + ctx: &mut BigNumContextRef, + ) -> Result<(), ErrorStack> { + unsafe { + cvt(ffi::BN_div( + ptr::null_mut(), + self.as_ptr(), + a.as_ptr(), + b.as_ptr(), + ctx.as_ptr(), + )) + .map(|_| ()) + } + } + + /// Places the result of `a / b` in `self` and `a % b` in `rem`. + /// + /// OpenSSL documentation at [`BN_div`] + /// + /// [`BN_div`]: https://www.openssl.org/docs/man1.1.0/crypto/BN_div.html + pub fn div_rem( + &mut self, + rem: &mut BigNumRef, + a: &BigNumRef, + b: &BigNumRef, + ctx: &mut BigNumContextRef, + ) -> Result<(), ErrorStack> { + unsafe { + cvt(ffi::BN_div( + self.as_ptr(), + rem.as_ptr(), + a.as_ptr(), + b.as_ptr(), + ctx.as_ptr(), + )) + .map(|_| ()) + } + } + + /// Places the result of `a²` in `self`. + /// + /// OpenSSL documentation at [`BN_sqr`] + /// + /// [`BN_sqr`]: https://www.openssl.org/docs/man1.1.0/crypto/BN_sqr.html + pub fn sqr(&mut self, a: &BigNumRef, ctx: &mut BigNumContextRef) -> Result<(), ErrorStack> { + unsafe { cvt(ffi::BN_sqr(self.as_ptr(), a.as_ptr(), ctx.as_ptr())).map(|_| ()) } + } + + /// Places the result of `a mod m` in `self`. As opposed to `div_rem` + /// the result is non-negative. + /// + /// OpenSSL documentation at [`BN_nnmod`] + /// + /// [`BN_nnmod`]: https://www.openssl.org/docs/man1.1.0/crypto/BN_nnmod.html + pub fn nnmod( + &mut self, + a: &BigNumRef, + m: &BigNumRef, + ctx: &mut BigNumContextRef, + ) -> Result<(), ErrorStack> { + unsafe { + cvt(ffi::BN_nnmod( + self.as_ptr(), + a.as_ptr(), + m.as_ptr(), + ctx.as_ptr(), + )) + .map(|_| ()) + } + } + + /// Places the result of `(a + b) mod m` in `self`. + /// + /// OpenSSL documentation at [`BN_mod_add`] + /// + /// [`BN_mod_add`]: https://www.openssl.org/docs/man1.1.0/crypto/BN_mod_add.html + pub fn mod_add( + &mut self, + a: &BigNumRef, + b: &BigNumRef, + m: &BigNumRef, + ctx: &mut BigNumContextRef, + ) -> Result<(), ErrorStack> { + unsafe { + cvt(ffi::BN_mod_add( + self.as_ptr(), + a.as_ptr(), + b.as_ptr(), + m.as_ptr(), + ctx.as_ptr(), + )) + .map(|_| ()) + } + } + + /// Places the result of `(a - b) mod m` in `self`. + /// + /// OpenSSL documentation at [`BN_mod_sub`] + /// + /// [`BN_mod_sub`]: https://www.openssl.org/docs/man1.1.0/crypto/BN_mod_sub.html + pub fn mod_sub( + &mut self, + a: &BigNumRef, + b: &BigNumRef, + m: &BigNumRef, + ctx: &mut BigNumContextRef, + ) -> Result<(), ErrorStack> { + unsafe { + cvt(ffi::BN_mod_sub( + self.as_ptr(), + a.as_ptr(), + b.as_ptr(), + m.as_ptr(), + ctx.as_ptr(), + )) + .map(|_| ()) + } + } + + /// Places the result of `(a * b) mod m` in `self`. + /// + /// OpenSSL documentation at [`BN_mod_mul`] + /// + /// [`BN_mod_mul`]: https://www.openssl.org/docs/man1.1.0/crypto/BN_mod_mul.html + pub fn mod_mul( + &mut self, + a: &BigNumRef, + b: &BigNumRef, + m: &BigNumRef, + ctx: &mut BigNumContextRef, + ) -> Result<(), ErrorStack> { + unsafe { + cvt(ffi::BN_mod_mul( + self.as_ptr(), + a.as_ptr(), + b.as_ptr(), + m.as_ptr(), + ctx.as_ptr(), + )) + .map(|_| ()) + } + } + + /// Places the result of `a² mod m` in `self`. + /// + /// OpenSSL documentation at [`BN_mod_sqr`] + /// + /// [`BN_mod_sqr`]: https://www.openssl.org/docs/man1.1.0/crypto/BN_mod_sqr.html + pub fn mod_sqr( + &mut self, + a: &BigNumRef, + m: &BigNumRef, + ctx: &mut BigNumContextRef, + ) -> Result<(), ErrorStack> { + unsafe { + cvt(ffi::BN_mod_sqr( + self.as_ptr(), + a.as_ptr(), + m.as_ptr(), + ctx.as_ptr(), + )) + .map(|_| ()) + } + } + + /// Places the result of `a^p` in `self`. + /// + /// OpenSSL documentation at [`BN_exp`] + /// + /// [`BN_exp`]: https://www.openssl.org/docs/man1.1.0/crypto/BN_exp.html + pub fn exp( + &mut self, + a: &BigNumRef, + p: &BigNumRef, + ctx: &mut BigNumContextRef, + ) -> Result<(), ErrorStack> { + unsafe { + cvt(ffi::BN_exp( + self.as_ptr(), + a.as_ptr(), + p.as_ptr(), + ctx.as_ptr(), + )) + .map(|_| ()) + } + } + + /// Places the result of `a^p mod m` in `self`. + /// + /// OpenSSL documentation at [`BN_mod_exp`] + /// + /// [`BN_mod_exp`]: https://www.openssl.org/docs/man1.1.0/crypto/BN_mod_exp.html + pub fn mod_exp( + &mut self, + a: &BigNumRef, + p: &BigNumRef, + m: &BigNumRef, + ctx: &mut BigNumContextRef, + ) -> Result<(), ErrorStack> { + unsafe { + cvt(ffi::BN_mod_exp( + self.as_ptr(), + a.as_ptr(), + p.as_ptr(), + m.as_ptr(), + ctx.as_ptr(), + )) + .map(|_| ()) + } + } + + /// Places the inverse of `a` modulo `n` in `self`. + pub fn mod_inverse( + &mut self, + a: &BigNumRef, + n: &BigNumRef, + ctx: &mut BigNumContextRef, + ) -> Result<(), ErrorStack> { + unsafe { + cvt_p(ffi::BN_mod_inverse( + self.as_ptr(), + a.as_ptr(), + n.as_ptr(), + ctx.as_ptr(), + )) + .map(|_| ()) + } + } + + /// Places the greatest common denominator of `a` and `b` in `self`. + /// + /// OpenSSL documentation at [`BN_gcd`] + /// + /// [`BN_gcd`]: https://www.openssl.org/docs/man1.1.0/crypto/BN_gcd.html + pub fn gcd( + &mut self, + a: &BigNumRef, + b: &BigNumRef, + ctx: &mut BigNumContextRef, + ) -> Result<(), ErrorStack> { + unsafe { + cvt(ffi::BN_gcd( + self.as_ptr(), + a.as_ptr(), + b.as_ptr(), + ctx.as_ptr(), + )) + .map(|_| ()) + } + } + + /// Checks whether `self` is prime. + /// + /// Performs a Miller-Rabin probabilistic primality test with `checks` iterations. + /// + /// OpenSSL documentation at [`BN_is_prime_ex`] + /// + /// [`BN_is_prime_ex`]: https://www.openssl.org/docs/man1.1.0/crypto/BN_is_prime_ex.html + /// + /// # Return Value + /// + /// Returns `true` if `self` is prime with an error probability of less than `0.25 ^ checks`. + #[allow(clippy::useless_conversion)] + pub fn is_prime(&self, checks: i32, ctx: &mut BigNumContextRef) -> Result { + unsafe { + cvt_n(ffi::BN_is_prime_ex( + self.as_ptr(), + checks.into(), + ctx.as_ptr(), + ptr::null_mut(), + )) + .map(|r| r != 0) + } + } + + /// Checks whether `self` is prime with optional trial division. + /// + /// If `do_trial_division` is `true`, first performs trial division by a number of small primes. + /// Then, like `is_prime`, performs a Miller-Rabin probabilistic primality test with `checks` + /// iterations. + /// + /// OpenSSL documentation at [`BN_is_prime_fasttest_ex`] + /// + /// [`BN_is_prime_fasttest_ex`]: https://www.openssl.org/docs/man1.1.0/crypto/BN_is_prime_fasttest_ex.html + /// + /// # Return Value + /// + /// Returns `true` if `self` is prime with an error probability of less than `0.25 ^ checks`. + #[allow(clippy::useless_conversion)] + pub fn is_prime_fasttest( + &self, + checks: i32, + ctx: &mut BigNumContextRef, + do_trial_division: bool, + ) -> Result { + unsafe { + cvt_n(ffi::BN_is_prime_fasttest_ex( + self.as_ptr(), + checks.into(), + ctx.as_ptr(), + do_trial_division as c_int, + ptr::null_mut(), + )) + .map(|r| r != 0) + } + } + + /// Returns a big-endian byte vector representation of the absolute value of `self`. + /// + /// `self` can be recreated by using `from_slice`. + /// + /// ``` + /// # use boring::bn::BigNum; + /// let s = -BigNum::from_u32(4543).unwrap(); + /// let r = BigNum::from_u32(4543).unwrap(); + /// + /// let s_vec = s.to_vec(); + /// assert_eq!(BigNum::from_slice(&s_vec).unwrap(), r); + /// ``` + pub fn to_vec(&self) -> Vec { + let size = self.num_bytes() as usize; + let mut v = Vec::with_capacity(size); + unsafe { + ffi::BN_bn2bin(self.as_ptr(), v.as_mut_ptr()); + v.set_len(size); + } + v + } + + /// Returns a big-endian byte vector representation of the absolute value of `self` padded + /// to `pad_to` bytes. + /// + /// If `pad_to` is less than `self.num_bytes()` then an error is returned. + /// + /// `self` can be recreated by using `from_slice`. + /// + /// ``` + /// # use boring::bn::BigNum; + /// let bn = BigNum::from_u32(0x4543).unwrap(); + /// + /// let bn_vec = bn.to_vec_padded(4).unwrap(); + /// assert_eq!(&bn_vec, &[0, 0, 0x45, 0x43]); + /// + /// let r = bn.to_vec_padded(1); + /// assert!(r.is_err()); + /// + /// let bn = -BigNum::from_u32(0x4543).unwrap(); + /// let bn_vec = bn.to_vec_padded(4).unwrap(); + /// assert_eq!(&bn_vec, &[0, 0, 0x45, 0x43]); + /// ``` + pub fn to_vec_padded(&self, pad_to: usize) -> Result, ErrorStack> { + let mut v = Vec::with_capacity(pad_to); + unsafe { + cvt(ffi::BN_bn2bin_padded(v.as_mut_ptr(), pad_to, self.as_ptr()))?; + v.set_len(pad_to); + } + Ok(v) + } + + /// Returns a decimal string representation of `self`. + /// + /// ``` + /// # use boring::bn::BigNum; + /// let s = -BigNum::from_u32(12345).unwrap(); + /// + /// assert_eq!(&**s.to_dec_str().unwrap(), "-12345"); + /// ``` + pub fn to_dec_str(&self) -> Result { + unsafe { + let buf = cvt_p(ffi::BN_bn2dec(self.as_ptr()))?; + Ok(OpensslString::from_ptr(buf)) + } + } + + /// Returns a hexadecimal string representation of `self`. + /// + /// ``` + /// # use boring::bn::BigNum; + /// let s = -BigNum::from_u32(0x99ff).unwrap(); + /// + /// assert_eq!(&**s.to_hex_str().unwrap(), "-99ff"); + /// ``` + pub fn to_hex_str(&self) -> Result { + unsafe { + let buf = cvt_p(ffi::BN_bn2hex(self.as_ptr()))?; + Ok(OpensslString::from_ptr(buf)) + } + } + + /// Returns an `Asn1Integer` containing the value of `self`. + pub fn to_asn1_integer(&self) -> Result { + unsafe { + cvt_p(ffi::BN_to_ASN1_INTEGER(self.as_ptr(), ptr::null_mut())) + .map(|p| Asn1Integer::from_ptr(p)) + } + } +} + +impl BigNum { + /// Creates a new `BigNum` with the value 0. + pub fn new() -> Result { + unsafe { + ffi::init(); + let v = cvt_p(ffi::BN_new())?; + Ok(BigNum::from_ptr(v)) + } + } + + /// Creates a new `BigNum` with the given value. + /// + /// OpenSSL documentation at [`BN_set_word`] + /// + /// [`BN_set_word`]: https://www.openssl.org/docs/man1.1.0/crypto/BN_set_word.html + pub fn from_u32(n: u32) -> Result { + BigNum::new().and_then(|v| unsafe { + cvt(ffi::BN_set_word(v.as_ptr(), n as ffi::BN_ULONG)).map(|_| v) + }) + } + + /// Creates a `BigNum` from a decimal string. + /// + /// OpenSSL documentation at [`BN_dec2bn`] + /// + /// [`BN_dec2bn`]: https://www.openssl.org/docs/man1.1.0/crypto/BN_dec2bn.html + pub fn from_dec_str(s: &str) -> Result { + unsafe { + ffi::init(); + let c_str = CString::new(s.as_bytes()).unwrap(); + let mut bn = ptr::null_mut(); + cvt(ffi::BN_dec2bn(&mut bn, c_str.as_ptr() as *const _))?; + Ok(BigNum::from_ptr(bn)) + } + } + + /// Creates a `BigNum` from a hexadecimal string. + /// + /// OpenSSL documentation at [`BN_hex2bn`] + /// + /// [`BN_hex2bn`]: https://www.openssl.org/docs/man1.1.0/crypto/BN_hex2bn.html + pub fn from_hex_str(s: &str) -> Result { + unsafe { + ffi::init(); + let c_str = CString::new(s.as_bytes()).unwrap(); + let mut bn = ptr::null_mut(); + cvt(ffi::BN_hex2bn(&mut bn, c_str.as_ptr() as *const _))?; + Ok(BigNum::from_ptr(bn)) + } + } + + /// Creates a new `BigNum` from an unsigned, big-endian encoded number of arbitrary length. + /// + /// OpenSSL documentation at [`BN_bin2bn`] + /// + /// [`BN_bin2bn`]: https://www.openssl.org/docs/man1.1.0/crypto/BN_bin2bn.html + /// + /// ``` + /// # use boring::bn::BigNum; + /// let bignum = BigNum::from_slice(&[0x12, 0x00, 0x34]).unwrap(); + /// + /// assert_eq!(bignum, BigNum::from_u32(0x120034).unwrap()); + /// ``` + pub fn from_slice(n: &[u8]) -> Result { + unsafe { + ffi::init(); + assert!(n.len() <= c_int::max_value() as usize); + cvt_p(ffi::BN_bin2bn( + n.as_ptr(), + n.len() as size_t, + ptr::null_mut(), + )) + .map(|p| BigNum::from_ptr(p)) + } + } +} + +impl fmt::Debug for BigNumRef { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match self.to_dec_str() { + Ok(s) => f.write_str(&s), + Err(e) => Err(e.into()), + } + } +} + +impl fmt::Debug for BigNum { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match self.to_dec_str() { + Ok(s) => f.write_str(&s), + Err(e) => Err(e.into()), + } + } +} + +impl fmt::Display for BigNumRef { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match self.to_dec_str() { + Ok(s) => f.write_str(&s), + Err(e) => Err(e.into()), + } + } +} + +impl fmt::Display for BigNum { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match self.to_dec_str() { + Ok(s) => f.write_str(&s), + Err(e) => Err(e.into()), + } + } +} + +impl PartialEq for BigNumRef { + fn eq(&self, oth: &BigNumRef) -> bool { + self.cmp(oth) == Ordering::Equal + } +} + +impl PartialEq for BigNumRef { + fn eq(&self, oth: &BigNum) -> bool { + self.eq(oth.deref()) + } +} + +impl Eq for BigNumRef {} + +impl PartialEq for BigNum { + fn eq(&self, oth: &BigNum) -> bool { + self.deref().eq(oth) + } +} + +impl PartialEq for BigNum { + fn eq(&self, oth: &BigNumRef) -> bool { + self.deref().eq(oth) + } +} + +impl Eq for BigNum {} + +impl PartialOrd for BigNumRef { + fn partial_cmp(&self, oth: &BigNumRef) -> Option { + Some(self.cmp(oth)) + } +} + +impl PartialOrd for BigNumRef { + fn partial_cmp(&self, oth: &BigNum) -> Option { + Some(self.cmp(oth.deref())) + } +} + +impl Ord for BigNumRef { + fn cmp(&self, oth: &BigNumRef) -> Ordering { + unsafe { ffi::BN_cmp(self.as_ptr(), oth.as_ptr()).cmp(&0) } + } +} + +impl PartialOrd for BigNum { + fn partial_cmp(&self, oth: &BigNum) -> Option { + Some(self.cmp(oth)) + } +} + +impl PartialOrd for BigNum { + fn partial_cmp(&self, oth: &BigNumRef) -> Option { + self.deref().partial_cmp(oth) + } +} + +impl Ord for BigNum { + fn cmp(&self, oth: &BigNum) -> Ordering { + self.deref().cmp(oth.deref()) + } +} + +macro_rules! delegate { + ($t:ident, $m:ident) => { + impl<'a, 'b> $t<&'b BigNum> for &'a BigNumRef { + type Output = BigNum; + + fn $m(self, oth: &BigNum) -> BigNum { + $t::$m(self, oth.deref()) + } + } + + impl<'a, 'b> $t<&'b BigNumRef> for &'a BigNum { + type Output = BigNum; + + fn $m(self, oth: &BigNumRef) -> BigNum { + $t::$m(self.deref(), oth) + } + } + + impl<'a, 'b> $t<&'b BigNum> for &'a BigNum { + type Output = BigNum; + + fn $m(self, oth: &BigNum) -> BigNum { + $t::$m(self.deref(), oth.deref()) + } + } + }; +} + +impl<'a, 'b> Add<&'b BigNumRef> for &'a BigNumRef { + type Output = BigNum; + + fn add(self, oth: &BigNumRef) -> BigNum { + let mut r = BigNum::new().unwrap(); + r.checked_add(self, oth).unwrap(); + r + } +} + +delegate!(Add, add); + +impl<'a, 'b> Sub<&'b BigNumRef> for &'a BigNumRef { + type Output = BigNum; + + fn sub(self, oth: &BigNumRef) -> BigNum { + let mut r = BigNum::new().unwrap(); + r.checked_sub(self, oth).unwrap(); + r + } +} + +delegate!(Sub, sub); + +impl<'a, 'b> Mul<&'b BigNumRef> for &'a BigNumRef { + type Output = BigNum; + + fn mul(self, oth: &BigNumRef) -> BigNum { + let mut ctx = BigNumContext::new().unwrap(); + let mut r = BigNum::new().unwrap(); + r.checked_mul(self, oth, &mut ctx).unwrap(); + r + } +} + +delegate!(Mul, mul); + +impl<'a, 'b> Div<&'b BigNumRef> for &'a BigNumRef { + type Output = BigNum; + + fn div(self, oth: &'b BigNumRef) -> BigNum { + let mut ctx = BigNumContext::new().unwrap(); + let mut r = BigNum::new().unwrap(); + r.checked_div(self, oth, &mut ctx).unwrap(); + r + } +} + +delegate!(Div, div); + +impl<'a, 'b> Rem<&'b BigNumRef> for &'a BigNumRef { + type Output = BigNum; + + fn rem(self, oth: &'b BigNumRef) -> BigNum { + let mut ctx = BigNumContext::new().unwrap(); + let mut r = BigNum::new().unwrap(); + r.checked_rem(self, oth, &mut ctx).unwrap(); + r + } +} + +delegate!(Rem, rem); + +impl<'a> Shl for &'a BigNumRef { + type Output = BigNum; + + fn shl(self, n: i32) -> BigNum { + let mut r = BigNum::new().unwrap(); + r.lshift(self, n).unwrap(); + r + } +} + +impl<'a> Shl for &'a BigNum { + type Output = BigNum; + + fn shl(self, n: i32) -> BigNum { + self.deref().shl(n) + } +} + +impl<'a> Shr for &'a BigNumRef { + type Output = BigNum; + + fn shr(self, n: i32) -> BigNum { + let mut r = BigNum::new().unwrap(); + r.rshift(self, n).unwrap(); + r + } +} + +impl<'a> Shr for &'a BigNum { + type Output = BigNum; + + fn shr(self, n: i32) -> BigNum { + self.deref().shr(n) + } +} + +impl<'a> Neg for &'a BigNumRef { + type Output = BigNum; + + fn neg(self) -> BigNum { + self.to_owned().unwrap().neg() + } +} + +impl<'a> Neg for &'a BigNum { + type Output = BigNum; + + fn neg(self) -> BigNum { + self.deref().neg() + } +} + +impl Neg for BigNum { + type Output = BigNum; + + fn neg(mut self) -> BigNum { + let negative = self.is_negative(); + self.set_negative(!negative); + self + } +} + +#[cfg(test)] +mod tests { + use crate::bn::{BigNum, BigNumContext}; + + #[test] + fn test_to_from_slice() { + let v0 = BigNum::from_u32(10_203_004).unwrap(); + let vec = v0.to_vec(); + let v1 = BigNum::from_slice(&vec).unwrap(); + + assert_eq!(v0, v1); + } + + #[test] + fn test_negation() { + let a = BigNum::from_u32(909_829_283).unwrap(); + + assert!(!a.is_negative()); + assert!((-a).is_negative()); + } + + #[test] + fn test_shift() { + let a = BigNum::from_u32(909_829_283).unwrap(); + + assert_eq!(a, &(&a << 1) >> 1); + } + + #[test] + fn test_rand_range() { + let range = BigNum::from_u32(909_829_283).unwrap(); + let mut result = BigNum::from_dec_str(&range.to_dec_str().unwrap()).unwrap(); + range.rand_range(&mut result).unwrap(); + assert!(result >= BigNum::from_u32(0).unwrap() && result < range); + } + + #[test] + fn test_pseudo_rand_range() { + let range = BigNum::from_u32(909_829_283).unwrap(); + let mut result = BigNum::from_dec_str(&range.to_dec_str().unwrap()).unwrap(); + range.pseudo_rand_range(&mut result).unwrap(); + assert!(result >= BigNum::from_u32(0).unwrap() && result < range); + } + + #[test] + fn test_prime_numbers() { + let a = BigNum::from_u32(19_029_017).unwrap(); + let mut p = BigNum::new().unwrap(); + p.generate_prime(128, true, None, Some(&a)).unwrap(); + + let mut ctx = BigNumContext::new().unwrap(); + assert!(p.is_prime(100, &mut ctx).unwrap()); + assert!(p.is_prime_fasttest(100, &mut ctx, true).unwrap()); + } +} diff --git a/crates/boring/src/conf.rs b/crates/boring/src/conf.rs new file mode 100644 index 000000000..c94dde4a8 --- /dev/null +++ b/crates/boring/src/conf.rs @@ -0,0 +1,39 @@ +//! Interface for processing OpenSSL configuration files. +use crate::ffi; +use foreign_types::ForeignType; +use libc::c_void; + +use crate::cvt_p; +use crate::error::ErrorStack; + +pub struct ConfMethod(*mut c_void); + +impl ConfMethod { + /// Construct from raw pointer. + /// + /// # Safety + /// + /// The caller must ensure that the pointer is valid. + pub unsafe fn from_ptr(ptr: *mut c_void) -> ConfMethod { + ConfMethod(ptr) + } + + /// Convert to raw pointer. + pub fn as_ptr(&self) -> *mut c_void { + self.0 + } +} + +foreign_type_and_impl_send_sync! { + type CType = ffi::CONF; + fn drop = ffi::NCONF_free; + + pub struct Conf; +} + +impl Conf { + /// Create a configuration parser. + pub fn new(method: ConfMethod) -> Result { + unsafe { cvt_p(ffi::NCONF_new(method.as_ptr())).map(|p| Conf::from_ptr(p)) } + } +} diff --git a/crates/boring/src/derive.rs b/crates/boring/src/derive.rs new file mode 100644 index 000000000..08a47dfb5 --- /dev/null +++ b/crates/boring/src/derive.rs @@ -0,0 +1,133 @@ +//! Shared secret derivation. +use crate::ffi; +use foreign_types::ForeignTypeRef; +use std::marker::PhantomData; +use std::ptr; + +use crate::error::ErrorStack; +use crate::pkey::{HasPrivate, HasPublic, PKeyRef}; +use crate::{cvt, cvt_p}; + +/// A type used to derive a shared secret between two keys. +pub struct Deriver<'a>(*mut ffi::EVP_PKEY_CTX, PhantomData<&'a ()>); + +unsafe impl<'a> Sync for Deriver<'a> {} +unsafe impl<'a> Send for Deriver<'a> {} + +impl<'a> Drop for Deriver<'a> { + fn drop(&mut self) { + unsafe { + ffi::EVP_PKEY_CTX_free(self.0); + } + } +} + +#[allow(clippy::len_without_is_empty)] +impl<'a> Deriver<'a> { + /// Creates a new `Deriver` using the provided private key. + /// + /// This corresponds to [`EVP_PKEY_derive_init`]. + /// + /// [`EVP_PKEY_derive_init`]: https://www.openssl.org/docs/man1.0.2/crypto/EVP_PKEY_derive_init.html + pub fn new(key: &'a PKeyRef) -> Result, ErrorStack> + where + T: HasPrivate, + { + unsafe { + cvt_p(ffi::EVP_PKEY_CTX_new(key.as_ptr(), ptr::null_mut())) + .map(|p| Deriver(p, PhantomData)) + .and_then(|ctx| cvt(ffi::EVP_PKEY_derive_init(ctx.0)).map(|_| ctx)) + } + } + + /// Sets the peer key used for secret derivation. + /// + /// This corresponds to [`EVP_PKEY_derive_set_peer`]: + /// + /// [`EVP_PKEY_derive_set_peer`]: https://www.openssl.org/docs/man1.0.2/crypto/EVP_PKEY_derive_init.html + pub fn set_peer(&mut self, key: &'a PKeyRef) -> Result<(), ErrorStack> + where + T: HasPublic, + { + unsafe { cvt(ffi::EVP_PKEY_derive_set_peer(self.0, key.as_ptr())).map(|_| ()) } + } + + /// Returns the size of the shared secret. + /// + /// It can be used to size the buffer passed to [`Deriver::derive`]. + /// + /// This corresponds to [`EVP_PKEY_derive`]. + /// + /// [`Deriver::derive`]: #method.derive + /// [`EVP_PKEY_derive`]: https://www.openssl.org/docs/man1.0.2/crypto/EVP_PKEY_derive_init.html + pub fn len(&mut self) -> Result { + unsafe { + let mut len = 0; + cvt(ffi::EVP_PKEY_derive(self.0, ptr::null_mut(), &mut len)).map(|_| len) + } + } + + /// Derives a shared secret between the two keys, writing it into the buffer. + /// + /// Returns the number of bytes written. + /// + /// This corresponds to [`EVP_PKEY_derive`]. + /// + /// [`EVP_PKEY_derive`]: https://www.openssl.org/docs/man1.0.2/crypto/EVP_PKEY_derive_init.html + pub fn derive(&mut self, buf: &mut [u8]) -> Result { + let mut len = buf.len(); + unsafe { + cvt(ffi::EVP_PKEY_derive( + self.0, + buf.as_mut_ptr() as *mut _, + &mut len, + )) + .map(|_| len) + } + } + + /// A convenience function which derives a shared secret and returns it in a new buffer. + /// + /// This simply wraps [`Deriver::len`] and [`Deriver::derive`]. + /// + /// [`Deriver::len`]: #method.len + /// [`Deriver::derive`]: #method.derive + pub fn derive_to_vec(&mut self) -> Result, ErrorStack> { + let len = self.len()?; + let mut buf = vec![0; len]; + let len = self.derive(&mut buf)?; + buf.truncate(len); + Ok(buf) + } +} + +#[cfg(test)] +mod test { + use super::*; + + use crate::ec::{EcGroup, EcKey}; + use crate::nid::Nid; + use crate::pkey::PKey; + + #[test] + fn derive_without_peer() { + let group = EcGroup::from_curve_name(Nid::X9_62_PRIME256V1).unwrap(); + let ec_key = EcKey::generate(&group).unwrap(); + let pkey = PKey::from_ec_key(ec_key).unwrap(); + let mut deriver = Deriver::new(&pkey).unwrap(); + deriver.derive_to_vec().unwrap_err(); + } + + #[test] + fn test_ec_key_derive() { + let group = EcGroup::from_curve_name(Nid::X9_62_PRIME256V1).unwrap(); + let ec_key = EcKey::generate(&group).unwrap(); + let ec_key2 = EcKey::generate(&group).unwrap(); + let pkey = PKey::from_ec_key(ec_key).unwrap(); + let pkey2 = PKey::from_ec_key(ec_key2).unwrap(); + let mut deriver = Deriver::new(&pkey).unwrap(); + deriver.set_peer(&pkey2).unwrap(); + let shared = deriver.derive_to_vec().unwrap(); + assert!(!shared.is_empty()); + } +} diff --git a/crates/boring/src/dh.rs b/crates/boring/src/dh.rs new file mode 100644 index 000000000..96a8c63d6 --- /dev/null +++ b/crates/boring/src/dh.rs @@ -0,0 +1,133 @@ +use crate::error::ErrorStack; +use crate::ffi; +use foreign_types::{ForeignType, ForeignTypeRef}; +use std::mem; +use std::ptr; + +use crate::bn::BigNum; +use crate::pkey::{HasParams, Params}; +use crate::{cvt, cvt_p}; + +generic_foreign_type_and_impl_send_sync! { + type CType = ffi::DH; + fn drop = ffi::DH_free; + + pub struct Dh; + + pub struct DhRef; +} + +impl DhRef +where + T: HasParams, +{ + to_pem! { + /// Serializes the parameters into a PEM-encoded PKCS#3 DHparameter structure. + /// + /// The output will have a header of `-----BEGIN DH PARAMETERS-----`. + /// + /// This corresponds to [`PEM_write_bio_DHparams`]. + /// + /// [`PEM_write_bio_DHparams`]: https://www.openssl.org/docs/manmaster/man3/PEM_write_bio_DHparams.html + params_to_pem, + ffi::PEM_write_bio_DHparams + } + + to_der! { + /// Serializes the parameters into a DER-encoded PKCS#3 DHparameter structure. + /// + /// This corresponds to [`i2d_DHparams`]. + /// + /// [`i2d_DHparams`]: https://www.openssl.org/docs/man1.1.0/crypto/i2d_DHparams.html + params_to_der, + ffi::i2d_DHparams + } +} + +impl Dh { + pub fn from_params(p: BigNum, g: BigNum, q: BigNum) -> Result, ErrorStack> { + unsafe { + let dh = Dh::from_ptr(cvt_p(ffi::DH_new())?); + cvt(DH_set0_pqg(dh.0, p.as_ptr(), q.as_ptr(), g.as_ptr()))?; + mem::forget((p, g, q)); + Ok(dh) + } + } + + from_pem! { + /// Deserializes a PEM-encoded PKCS#3 DHpararameters structure. + /// + /// The input should have a header of `-----BEGIN DH PARAMETERS-----`. + /// + /// This corresponds to [`PEM_read_bio_DHparams`]. + /// + /// [`PEM_read_bio_DHparams`]: https://www.openssl.org/docs/man1.0.2/crypto/PEM_read_bio_DHparams.html + params_from_pem, + Dh, + ffi::PEM_read_bio_DHparams + } + + from_der! { + /// Deserializes a DER-encoded PKCS#3 DHparameters structure. + /// + /// This corresponds to [`d2i_DHparams`]. + /// + /// [`d2i_DHparams`]: https://www.openssl.org/docs/man1.1.0/crypto/d2i_DHparams.html + params_from_der, + Dh, + ffi::d2i_DHparams, + ::libc::c_long + } +} + +use crate::ffi::DH_set0_pqg; + +#[cfg(test)] +mod tests { + use crate::bn::BigNum; + use crate::dh::Dh; + use crate::ssl::{SslContext, SslMethod}; + + #[test] + fn test_dh() { + let mut ctx = SslContext::builder(SslMethod::tls()).unwrap(); + let p = BigNum::from_hex_str( + "87A8E61DB4B6663CFFBBD19C651959998CEEF608660DD0F25D2CEED4435E3B00E00DF8F1D61957D4FAF7DF\ + 4561B2AA3016C3D91134096FAA3BF4296D830E9A7C209E0C6497517ABD5A8A9D306BCF67ED91F9E6725B47\ + 58C022E0B1EF4275BF7B6C5BFC11D45F9088B941F54EB1E59BB8BC39A0BF12307F5C4FDB70C581B23F76B6\ + 3ACAE1CAA6B7902D52526735488A0EF13C6D9A51BFA4AB3AD8347796524D8EF6A167B5A41825D967E144E5\ + 140564251CCACB83E6B486F6B3CA3F7971506026C0B857F689962856DED4010ABD0BE621C3A3960A54E710\ + C375F26375D7014103A4B54330C198AF126116D2276E11715F693877FAD7EF09CADB094AE91E1A1597", + ).unwrap(); + let g = BigNum::from_hex_str( + "3FB32C9B73134D0B2E77506660EDBD484CA7B18F21EF205407F4793A1A0BA12510DBC15077BE463FFF4FED\ + 4AAC0BB555BE3A6C1B0C6B47B1BC3773BF7E8C6F62901228F8C28CBB18A55AE31341000A650196F931C77A\ + 57F2DDF463E5E9EC144B777DE62AAAB8A8628AC376D282D6ED3864E67982428EBC831D14348F6F2F9193B5\ + 045AF2767164E1DFC967C1FB3F2E55A4BD1BFFE83B9C80D052B985D182EA0ADB2A3B7313D3FE14C8484B1E\ + 052588B9B7D2BBD2DF016199ECD06E1557CD0915B3353BBB64E0EC377FD028370DF92B52C7891428CDC67E\ + B6184B523D1DB246C32F63078490F00EF8D647D148D47954515E2327CFEF98C582664B4C0F6CC41659", + ).unwrap(); + let q = BigNum::from_hex_str( + "8CF83642A709A097B447997640129DA299B1A47D1EB3750BA308B0FE64F5FBD3", + ) + .unwrap(); + let dh = Dh::from_params(p, g, q).unwrap(); + ctx.set_tmp_dh(&dh).unwrap(); + } + + #[test] + fn test_dh_from_pem() { + let mut ctx = SslContext::builder(SslMethod::tls()).unwrap(); + let params = include_bytes!("../test/dhparams.pem"); + let dh = Dh::params_from_pem(params).unwrap(); + ctx.set_tmp_dh(&dh).unwrap(); + } + + #[test] + fn test_dh_from_der() { + let params = include_bytes!("../test/dhparams.pem"); + let dh = Dh::params_from_pem(params).unwrap(); + let der = dh.params_to_der().unwrap(); + Dh::params_from_der(&der).unwrap(); + } +} diff --git a/crates/boring/src/dsa.rs b/crates/boring/src/dsa.rs new file mode 100644 index 000000000..60c0c1970 --- /dev/null +++ b/crates/boring/src/dsa.rs @@ -0,0 +1,357 @@ +//! Digital Signatures +//! +//! DSA ensures a message originated from a known sender, and was not modified. +//! DSA uses asymetrical keys and an algorithm to output a signature of the message +//! using the private key that can be validated with the public key but not be generated +//! without the private key. + +use crate::ffi; +use foreign_types::{ForeignType, ForeignTypeRef}; +use libc::c_uint; +use std::fmt; +use std::mem; +use std::ptr; + +use crate::bn::{BigNum, BigNumRef}; +use crate::error::ErrorStack; +use crate::pkey::{HasParams, HasPrivate, HasPublic, Private, Public}; +use crate::{cvt, cvt_p}; + +generic_foreign_type_and_impl_send_sync! { + type CType = ffi::DSA; + fn drop = ffi::DSA_free; + + /// Object representing DSA keys. + /// + /// A DSA object contains the parameters p, q, and g. There is a private + /// and public key. The values p, g, and q are: + /// + /// * `p`: DSA prime parameter + /// * `q`: DSA sub-prime parameter + /// * `g`: DSA base parameter + /// + /// These values are used to calculate a pair of asymetrical keys used for + /// signing. + /// + /// OpenSSL documentation at [`DSA_new`] + /// + /// [`DSA_new`]: https://www.openssl.org/docs/man1.1.0/crypto/DSA_new.html + /// + /// # Examples + /// + /// ``` + /// use boring::dsa::Dsa; + /// use boring::error::ErrorStack; + /// use boring::pkey::Private; + /// + /// fn create_dsa() -> Result, ErrorStack> { + /// let sign = Dsa::generate(2048)?; + /// Ok(sign) + /// } + /// # fn main() { + /// # create_dsa(); + /// # } + /// ``` + pub struct Dsa; + /// Reference to [`Dsa`]. + /// + /// [`Dsa`]: struct.Dsa.html + pub struct DsaRef; +} + +impl Clone for Dsa { + fn clone(&self) -> Dsa { + (**self).to_owned() + } +} + +impl ToOwned for DsaRef { + type Owned = Dsa; + + fn to_owned(&self) -> Dsa { + unsafe { + ffi::DSA_up_ref(self.as_ptr()); + Dsa::from_ptr(self.as_ptr()) + } + } +} + +impl DsaRef +where + T: HasPublic, +{ + to_pem! { + /// Serialies the public key into a PEM-encoded SubjectPublicKeyInfo structure. + /// + /// The output will have a header of `-----BEGIN PUBLIC KEY-----`. + /// + /// This corresponds to [`PEM_write_bio_DSA_PUBKEY`]. + /// + /// [`PEM_write_bio_DSA_PUBKEY`]: https://www.openssl.org/docs/man1.1.0/crypto/PEM_write_bio_DSA_PUBKEY.html + public_key_to_pem, + ffi::PEM_write_bio_DSA_PUBKEY + } + + to_der! { + /// Serializes the public key into a DER-encoded SubjectPublicKeyInfo structure. + /// + /// This corresponds to [`i2d_DSA_PUBKEY`]. + /// + /// [`i2d_DSA_PUBKEY`]: https://www.openssl.org/docs/man1.1.0/crypto/i2d_DSA_PUBKEY.html + public_key_to_der, + ffi::i2d_DSA_PUBKEY + } + + /// Returns a reference to the public key component of `self`. + pub fn pub_key(&self) -> &BigNumRef { + unsafe { + let mut pub_key = ptr::null(); + DSA_get0_key(self.as_ptr(), &mut pub_key, ptr::null_mut()); + BigNumRef::from_ptr(pub_key as *mut _) + } + } +} + +impl DsaRef +where + T: HasPrivate, +{ + private_key_to_pem! { + /// Serializes the private key to a PEM-encoded DSAPrivateKey structure. + /// + /// The output will have a header of `-----BEGIN DSA PRIVATE KEY-----`. + /// + /// This corresponds to [`PEM_write_bio_DSAPrivateKey`]. + /// + /// [`PEM_write_bio_DSAPrivateKey`]: https://www.openssl.org/docs/man1.1.0/crypto/PEM_write_bio_DSAPrivateKey.html + private_key_to_pem, + /// Serializes the private key to a PEM-encoded encrypted DSAPrivateKey structure. + /// + /// The output will have a header of `-----BEGIN DSA PRIVATE KEY-----`. + /// + /// This corresponds to [`PEM_write_bio_DSAPrivateKey`]. + /// + /// [`PEM_write_bio_DSAPrivateKey`]: https://www.openssl.org/docs/man1.1.0/crypto/PEM_write_bio_DSAPrivateKey.html + private_key_to_pem_passphrase, + ffi::PEM_write_bio_DSAPrivateKey + } + + /// Returns a reference to the private key component of `self`. + pub fn priv_key(&self) -> &BigNumRef { + unsafe { + let mut priv_key = ptr::null(); + DSA_get0_key(self.as_ptr(), ptr::null_mut(), &mut priv_key); + BigNumRef::from_ptr(priv_key as *mut _) + } + } +} + +impl DsaRef +where + T: HasParams, +{ + /// Returns the maximum size of the signature output by `self` in bytes. + /// + /// OpenSSL documentation at [`DSA_size`] + /// + /// [`DSA_size`]: https://www.openssl.org/docs/man1.1.0/crypto/DSA_size.html + pub fn size(&self) -> u32 { + unsafe { ffi::DSA_size(self.as_ptr()) as u32 } + } + + /// Returns the DSA prime parameter of `self`. + pub fn p(&self) -> &BigNumRef { + unsafe { + let mut p = ptr::null(); + DSA_get0_pqg(self.as_ptr(), &mut p, ptr::null_mut(), ptr::null_mut()); + BigNumRef::from_ptr(p as *mut _) + } + } + + /// Returns the DSA sub-prime parameter of `self`. + pub fn q(&self) -> &BigNumRef { + unsafe { + let mut q = ptr::null(); + DSA_get0_pqg(self.as_ptr(), ptr::null_mut(), &mut q, ptr::null_mut()); + BigNumRef::from_ptr(q as *mut _) + } + } + + /// Returns the DSA base parameter of `self`. + pub fn g(&self) -> &BigNumRef { + unsafe { + let mut g = ptr::null(); + DSA_get0_pqg(self.as_ptr(), ptr::null_mut(), ptr::null_mut(), &mut g); + BigNumRef::from_ptr(g as *mut _) + } + } +} + +impl Dsa { + /// Generate a DSA key pair. + /// + /// Calls [`DSA_generate_parameters_ex`] to populate the `p`, `g`, and `q` values. + /// These values are used to generate the key pair with [`DSA_generate_key`]. + /// + /// The `bits` parameter corresponds to the length of the prime `p`. + /// + /// [`DSA_generate_parameters_ex`]: https://www.openssl.org/docs/man1.1.0/crypto/DSA_generate_parameters_ex.html + /// [`DSA_generate_key`]: https://www.openssl.org/docs/man1.1.0/crypto/DSA_generate_key.html + pub fn generate(bits: u32) -> Result, ErrorStack> { + ffi::init(); + unsafe { + let dsa = Dsa::from_ptr(cvt_p(ffi::DSA_new())?); + cvt(ffi::DSA_generate_parameters_ex( + dsa.0, + bits as c_uint, + ptr::null(), + 0, + ptr::null_mut(), + ptr::null_mut(), + ptr::null_mut(), + ))?; + cvt(ffi::DSA_generate_key(dsa.0))?; + Ok(dsa) + } + } + + /// Create a DSA key pair with the given parameters + /// + /// `p`, `q` and `g` are the common parameters. + /// `priv_key` is the private component of the key pair. + /// `pub_key` is the public component of the key. Can be computed via `g^(priv_key) mod p` + pub fn from_private_components( + p: BigNum, + q: BigNum, + g: BigNum, + priv_key: BigNum, + pub_key: BigNum, + ) -> Result, ErrorStack> { + ffi::init(); + unsafe { + let dsa = Dsa::from_ptr(cvt_p(ffi::DSA_new())?); + cvt(DSA_set0_pqg(dsa.0, p.as_ptr(), q.as_ptr(), g.as_ptr()))?; + mem::forget((p, q, g)); + cvt(DSA_set0_key(dsa.0, pub_key.as_ptr(), priv_key.as_ptr()))?; + mem::forget((pub_key, priv_key)); + Ok(dsa) + } + } +} + +impl Dsa { + from_pem! { + /// Decodes a PEM-encoded SubjectPublicKeyInfo structure containing a DSA key. + /// + /// The input should have a header of `-----BEGIN PUBLIC KEY-----`. + /// + /// This corresponds to [`PEM_read_bio_DSA_PUBKEY`]. + /// + /// [`PEM_read_bio_DSA_PUBKEY`]: https://www.openssl.org/docs/man1.0.2/crypto/PEM_read_bio_DSA_PUBKEY.html + public_key_from_pem, + Dsa, + ffi::PEM_read_bio_DSA_PUBKEY + } + + from_der! { + /// Decodes a DER-encoded SubjectPublicKeyInfo structure containing a DSA key. + /// + /// This corresponds to [`d2i_DSA_PUBKEY`]. + /// + /// [`d2i_DSA_PUBKEY`]: https://www.openssl.org/docs/man1.0.2/crypto/d2i_DSA_PUBKEY.html + public_key_from_der, + Dsa, + ffi::d2i_DSA_PUBKEY, + ::libc::c_long + } + + /// Create a new DSA key with only public components. + /// + /// `p`, `q` and `g` are the common parameters. + /// `pub_key` is the public component of the key. + pub fn from_public_components( + p: BigNum, + q: BigNum, + g: BigNum, + pub_key: BigNum, + ) -> Result, ErrorStack> { + ffi::init(); + unsafe { + let dsa = Dsa::from_ptr(cvt_p(ffi::DSA_new())?); + cvt(DSA_set0_pqg(dsa.0, p.as_ptr(), q.as_ptr(), g.as_ptr()))?; + mem::forget((p, q, g)); + cvt(DSA_set0_key(dsa.0, pub_key.as_ptr(), ptr::null_mut()))?; + mem::forget(pub_key); + Ok(dsa) + } + } +} + +impl fmt::Debug for Dsa { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "DSA") + } +} + +use crate::ffi::{DSA_get0_key, DSA_get0_pqg, DSA_set0_key, DSA_set0_pqg}; + +#[cfg(test)] +mod test { + use super::*; + use crate::bn::BigNumContext; + + #[test] + pub fn test_generate() { + Dsa::generate(1024).unwrap(); + } + + #[test] + fn test_pubkey_generation() { + let dsa = Dsa::generate(1024).unwrap(); + let p = dsa.p(); + let g = dsa.g(); + let priv_key = dsa.priv_key(); + let pub_key = dsa.pub_key(); + let mut ctx = BigNumContext::new().unwrap(); + let mut calc = BigNum::new().unwrap(); + calc.mod_exp(g, priv_key, p, &mut ctx).unwrap(); + assert_eq!(&calc, pub_key) + } + + #[test] + fn test_priv_key_from_parts() { + let p = BigNum::from_u32(283).unwrap(); + let q = BigNum::from_u32(47).unwrap(); + let g = BigNum::from_u32(60).unwrap(); + let priv_key = BigNum::from_u32(15).unwrap(); + let pub_key = BigNum::from_u32(207).unwrap(); + + let dsa = Dsa::from_private_components(p, q, g, priv_key, pub_key).unwrap(); + assert_eq!(dsa.pub_key(), &BigNum::from_u32(207).unwrap()); + assert_eq!(dsa.priv_key(), &BigNum::from_u32(15).unwrap()); + assert_eq!(dsa.p(), &BigNum::from_u32(283).unwrap()); + assert_eq!(dsa.q(), &BigNum::from_u32(47).unwrap()); + assert_eq!(dsa.g(), &BigNum::from_u32(60).unwrap()); + } + + #[test] + fn test_pub_key_from_parts() { + let p = BigNum::from_u32(283).unwrap(); + let q = BigNum::from_u32(47).unwrap(); + let g = BigNum::from_u32(60).unwrap(); + let pub_key = BigNum::from_u32(207).unwrap(); + + let dsa = Dsa::from_public_components(p, q, g, pub_key).unwrap(); + assert_eq!(dsa.pub_key(), &BigNum::from_u32(207).unwrap()); + assert_eq!(dsa.p(), &BigNum::from_u32(283).unwrap()); + assert_eq!(dsa.q(), &BigNum::from_u32(47).unwrap()); + assert_eq!(dsa.g(), &BigNum::from_u32(60).unwrap()); + } + + #[test] + #[allow(clippy::redundant_clone)] + fn clone() { + let key = Dsa::generate(2048).unwrap(); + drop(key.clone()); + } +} diff --git a/crates/boring/src/ec.rs b/crates/boring/src/ec.rs new file mode 100644 index 000000000..9c80b2de5 --- /dev/null +++ b/crates/boring/src/ec.rs @@ -0,0 +1,1017 @@ +//! Elliptic Curve +//! +//! Cryptology relies on the difficulty of solving mathematical problems, such as the factor +//! of large integers composed of two large prime numbers and the discrete logarithm of a +//! random eliptic curve. This module provides low-level features of the latter. +//! Elliptic Curve protocols can provide the same security with smaller keys. +//! +//! There are 2 forms of elliptic curves, `Fp` and `F2^m`. These curves use irreducible +//! trinomial or pentanomial . Being a generic interface to a wide range of algorithms, +//! the cuves are generally referenced by [`EcGroup`]. There are many built in groups +//! found in [`Nid`]. +//! +//! OpenSSL Wiki explains the fields and curves in detail at [Eliptic Curve Cryptography]. +//! +//! [`EcGroup`]: struct.EcGroup.html +//! [`Nid`]: ../nid/struct.Nid.html +//! [Eliptic Curve Cryptography]: https://wiki.openssl.org/index.php/Elliptic_Curve_Cryptography +use crate::ffi; +use foreign_types::{ForeignType, ForeignTypeRef}; +use libc::c_int; +use std::fmt; +use std::ptr; + +use crate::bn::{BigNumContextRef, BigNumRef}; +use crate::error::ErrorStack; +use crate::nid::Nid; +use crate::pkey::{HasParams, HasPrivate, HasPublic, Params, Private, Public}; +use crate::{cvt, cvt_n, cvt_p, init}; + +/// Compressed or Uncompressed conversion +/// +/// Conversion from the binary value of the point on the curve is performed in one of +/// compressed, uncompressed, or hybrid conversions. The default is compressed, except +/// for binary curves. +/// +/// Further documentation is available in the [X9.62] standard. +/// +/// [X9.62]: http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.202.2977&rep=rep1&type=pdf +#[derive(Copy, Clone)] +pub struct PointConversionForm(ffi::point_conversion_form_t); + +impl PointConversionForm { + /// Compressed conversion from point value. + pub const COMPRESSED: PointConversionForm = + PointConversionForm(ffi::point_conversion_form_t::POINT_CONVERSION_COMPRESSED); + + /// Uncompressed conversion from point value. + pub const UNCOMPRESSED: PointConversionForm = + PointConversionForm(ffi::point_conversion_form_t::POINT_CONVERSION_UNCOMPRESSED); + + /// Performs both compressed and uncompressed conversions. + pub const HYBRID: PointConversionForm = + PointConversionForm(ffi::point_conversion_form_t::POINT_CONVERSION_HYBRID); +} + +/// Named Curve or Explicit +/// +/// This type acts as a boolean as to whether the `EcGroup` is named or explicit. +#[derive(Copy, Clone)] +pub struct Asn1Flag(c_int); + +impl Asn1Flag { + /// Curve defined using polynomial parameters + /// + /// Most applications use a named EC_GROUP curve, however, support + /// is included to explicitly define the curve used to calculate keys + /// This information would need to be known by both endpoint to make communication + /// effective. + /// + /// OPENSSL_EC_EXPLICIT_CURVE, but that was only added in 1.1. + /// Man page documents that 0 can be used in older versions. + /// + /// OpenSSL documentation at [`EC_GROUP`] + /// + /// [`EC_GROUP`]: https://www.openssl.org/docs/man1.1.0/crypto/EC_GROUP_get_seed_len.html + pub const EXPLICIT_CURVE: Asn1Flag = Asn1Flag(0); + + /// Standard Curves + /// + /// Curves that make up the typical encryption use cases. The collection of curves + /// are well known but extensible. + /// + /// OpenSSL documentation at [`EC_GROUP`] + /// + /// [`EC_GROUP`]: https://www.openssl.org/docs/manmaster/man3/EC_GROUP_order_bits.html + pub const NAMED_CURVE: Asn1Flag = Asn1Flag(ffi::OPENSSL_EC_NAMED_CURVE); +} + +foreign_type_and_impl_send_sync! { + type CType = ffi::EC_GROUP; + fn drop = ffi::EC_GROUP_free; + + /// Describes the curve + /// + /// A curve can be of the named curve type. These curves can be discovered + /// using openssl binary `openssl ecparam -list_curves`. Other operations + /// are available in the [wiki]. These named curves are available in the + /// [`Nid`] module. + /// + /// Curves can also be generated using prime field parameters or a binary field. + /// + /// Prime fields use the formula `y^2 mod p = x^3 + ax + b mod p`. Binary + /// fields use the formula `y^2 + xy = x^3 + ax^2 + b`. Named curves have + /// assured security. To prevent accidental vulnerabilities, they should + /// be prefered. + /// + /// [wiki]: https://wiki.openssl.org/index.php/Command_Line_Elliptic_Curve_Operations + /// [`Nid`]: ../nid/index.html + pub struct EcGroup; +} + +impl EcGroup { + /// Returns the group of a standard named curve. + /// + /// OpenSSL documentation at [`EC_GROUP_new`]. + /// + /// [`EC_GROUP_new`]: https://www.openssl.org/docs/man1.1.0/crypto/EC_GROUP_new.html + pub fn from_curve_name(nid: Nid) -> Result { + unsafe { + init(); + cvt_p(ffi::EC_GROUP_new_by_curve_name(nid.as_raw())).map(|p| EcGroup::from_ptr(p)) + } + } +} + +impl EcGroupRef { + /// Places the components of a curve over a prime field in the provided `BigNum`s. + /// The components make up the formula `y^2 mod p = x^3 + ax + b mod p`. + /// + /// OpenSSL documentation available at [`EC_GROUP_get_curve_GFp`] + /// + /// [`EC_GROUP_get_curve_GFp`]: https://www.openssl.org/docs/man1.1.0/crypto/EC_GROUP_get_curve_GFp.html + pub fn components_gfp( + &self, + p: &mut BigNumRef, + a: &mut BigNumRef, + b: &mut BigNumRef, + ctx: &mut BigNumContextRef, + ) -> Result<(), ErrorStack> { + unsafe { + cvt(ffi::EC_GROUP_get_curve_GFp( + self.as_ptr(), + p.as_ptr(), + a.as_ptr(), + b.as_ptr(), + ctx.as_ptr(), + )) + .map(|_| ()) + } + } + + /// Places the cofactor of the group in the provided `BigNum`. + /// + /// OpenSSL documentation at [`EC_GROUP_get_cofactor`] + /// + /// [`EC_GROUP_get_cofactor`]: https://www.openssl.org/docs/man1.1.0/crypto/EC_GROUP_get_cofactor.html + pub fn cofactor( + &self, + cofactor: &mut BigNumRef, + ctx: &mut BigNumContextRef, + ) -> Result<(), ErrorStack> { + unsafe { + cvt(ffi::EC_GROUP_get_cofactor( + self.as_ptr(), + cofactor.as_ptr(), + ctx.as_ptr(), + )) + .map(|_| ()) + } + } + + /// Returns the degree of the curve. + /// + /// OpenSSL documentation at [`EC_GROUP_get_degree`] + /// + /// [`EC_GROUP_get_degree`]: https://www.openssl.org/docs/man1.1.0/crypto/EC_GROUP_get_degree.html + #[allow(clippy::unnecessary_cast)] + pub fn degree(&self) -> u32 { + unsafe { ffi::EC_GROUP_get_degree(self.as_ptr()) as u32 } + } + + /// Returns the number of bits in the group order. + /// + /// OpenSSL documentation at [`EC_GROUP_order_bits`] + /// + /// [`EC_GROUP_order_bits`]: https://www.openssl.org/docs/man1.1.0/crypto/EC_GROUP_order_bits.html + pub fn order_bits(&self) -> u32 { + unsafe { ffi::EC_GROUP_order_bits(self.as_ptr()) as u32 } + } + + /// Returns the generator for the given curve as a [`EcPoint`]. + /// + /// OpenSSL documentation at [`EC_GROUP_get0_generator`] + /// + /// [`EC_GROUP_get0_generator`]: https://www.openssl.org/docs/man1.1.0/man3/EC_GROUP_get0_generator.html + pub fn generator(&self) -> &EcPointRef { + unsafe { + let ptr = ffi::EC_GROUP_get0_generator(self.as_ptr()); + EcPointRef::from_ptr(ptr as *mut _) + } + } + + /// Places the order of the curve in the provided `BigNum`. + /// + /// OpenSSL documentation at [`EC_GROUP_get_order`] + /// + /// [`EC_GROUP_get_order`]: https://www.openssl.org/docs/man1.1.0/crypto/EC_GROUP_get_order.html + pub fn order( + &self, + order: &mut BigNumRef, + ctx: &mut BigNumContextRef, + ) -> Result<(), ErrorStack> { + unsafe { + cvt(ffi::EC_GROUP_get_order( + self.as_ptr(), + order.as_ptr(), + ctx.as_ptr(), + )) + .map(|_| ()) + } + } + + /// Sets the flag determining if the group corresponds to a named curve or must be explicitly + /// parameterized. + /// + /// This defaults to `EXPLICIT_CURVE` in OpenSSL 1.0.1 and 1.0.2, but `NAMED_CURVE` in OpenSSL + /// 1.1.0. + pub fn set_asn1_flag(&mut self, flag: Asn1Flag) { + unsafe { + ffi::EC_GROUP_set_asn1_flag(self.as_ptr(), flag.0); + } + } + + /// Returns the name of the curve, if a name is associated. + /// + /// OpenSSL documentation at [`EC_GROUP_get_curve_name`] + /// + /// [`EC_GROUP_get_curve_name`]: https://www.openssl.org/docs/man1.1.0/crypto/EC_GROUP_get_curve_name.html + pub fn curve_name(&self) -> Option { + let nid = unsafe { ffi::EC_GROUP_get_curve_name(self.as_ptr()) }; + if nid > 0 { + Some(Nid::from_raw(nid)) + } else { + None + } + } +} + +foreign_type_and_impl_send_sync! { + type CType = ffi::EC_POINT; + fn drop = ffi::EC_POINT_free; + + /// Represents a point on the curve + /// + /// OpenSSL documentation at [`EC_POINT_new`] + /// + /// [`EC_POINT_new`]: https://www.openssl.org/docs/man1.1.0/crypto/EC_POINT_new.html + pub struct EcPoint; +} + +impl EcPointRef { + /// Computes `a + b`, storing the result in `self`. + /// + /// OpenSSL documentation at [`EC_POINT_add`] + /// + /// [`EC_POINT_add`]: https://www.openssl.org/docs/man1.1.0/crypto/EC_POINT_add.html + pub fn add( + &mut self, + group: &EcGroupRef, + a: &EcPointRef, + b: &EcPointRef, + ctx: &mut BigNumContextRef, + ) -> Result<(), ErrorStack> { + unsafe { + cvt(ffi::EC_POINT_add( + group.as_ptr(), + self.as_ptr(), + a.as_ptr(), + b.as_ptr(), + ctx.as_ptr(), + )) + .map(|_| ()) + } + } + + /// Computes `q * m`, storing the result in `self`. + /// + /// OpenSSL documentation at [`EC_POINT_mul`] + /// + /// [`EC_POINT_mul`]: https://www.openssl.org/docs/man1.1.0/crypto/EC_POINT_mul.html + pub fn mul( + &mut self, + group: &EcGroupRef, + q: &EcPointRef, + m: &BigNumRef, + // FIXME should be &mut + ctx: &BigNumContextRef, + ) -> Result<(), ErrorStack> { + unsafe { + cvt(ffi::EC_POINT_mul( + group.as_ptr(), + self.as_ptr(), + ptr::null(), + q.as_ptr(), + m.as_ptr(), + ctx.as_ptr(), + )) + .map(|_| ()) + } + } + + /// Computes `generator * n`, storing the result in `self`. + pub fn mul_generator( + &mut self, + group: &EcGroupRef, + n: &BigNumRef, + // FIXME should be &mut + ctx: &BigNumContextRef, + ) -> Result<(), ErrorStack> { + unsafe { + cvt(ffi::EC_POINT_mul( + group.as_ptr(), + self.as_ptr(), + n.as_ptr(), + ptr::null(), + ptr::null(), + ctx.as_ptr(), + )) + .map(|_| ()) + } + } + + /// Computes `generator * n + q * m`, storing the result in `self`. + pub fn mul_full( + &mut self, + group: &EcGroupRef, + n: &BigNumRef, + q: &EcPointRef, + m: &BigNumRef, + ctx: &mut BigNumContextRef, + ) -> Result<(), ErrorStack> { + unsafe { + cvt(ffi::EC_POINT_mul( + group.as_ptr(), + self.as_ptr(), + n.as_ptr(), + q.as_ptr(), + m.as_ptr(), + ctx.as_ptr(), + )) + .map(|_| ()) + } + } + + /// Inverts `self`. + /// + /// OpenSSL documentation at [`EC_POINT_invert`] + /// + /// [`EC_POINT_invert`]: https://www.openssl.org/docs/man1.1.0/crypto/EC_POINT_invert.html + pub fn invert(&mut self, group: &EcGroupRef, ctx: &BigNumContextRef) -> Result<(), ErrorStack> { + unsafe { + cvt(ffi::EC_POINT_invert( + group.as_ptr(), + self.as_ptr(), + ctx.as_ptr(), + )) + .map(|_| ()) + } + } + + /// Serializes the point to a binary representation. + /// + /// OpenSSL documentation at [`EC_POINT_point2oct`] + /// + /// [`EC_POINT_point2oct`]: https://www.openssl.org/docs/man1.1.0/crypto/EC_POINT_point2oct.html + pub fn to_bytes( + &self, + group: &EcGroupRef, + form: PointConversionForm, + ctx: &mut BigNumContextRef, + ) -> Result, ErrorStack> { + unsafe { + let len = ffi::EC_POINT_point2oct( + group.as_ptr(), + self.as_ptr(), + form.0, + ptr::null_mut(), + 0, + ctx.as_ptr(), + ); + if len == 0 { + return Err(ErrorStack::get()); + } + let mut buf = vec![0; len]; + let len = ffi::EC_POINT_point2oct( + group.as_ptr(), + self.as_ptr(), + form.0, + buf.as_mut_ptr(), + len, + ctx.as_ptr(), + ); + if len == 0 { + Err(ErrorStack::get()) + } else { + Ok(buf) + } + } + } + + /// Creates a new point on the specified curve with the same value. + /// + /// OpenSSL documentation at [`EC_POINT_dup`] + /// + /// [`EC_POINT_dup`]: https://www.openssl.org/docs/man1.1.0/crypto/EC_POINT_dup.html + pub fn to_owned(&self, group: &EcGroupRef) -> Result { + unsafe { + cvt_p(ffi::EC_POINT_dup(self.as_ptr(), group.as_ptr())).map(|p| EcPoint::from_ptr(p)) + } + } + + /// Determines if this point is equal to another. + /// + /// OpenSSL doucmentation at [`EC_POINT_cmp`] + /// + /// [`EC_POINT_cmp`]: https://www.openssl.org/docs/man1.1.0/crypto/EC_POINT_cmp.html + pub fn eq( + &self, + group: &EcGroupRef, + other: &EcPointRef, + ctx: &mut BigNumContextRef, + ) -> Result { + unsafe { + let res = cvt_n(ffi::EC_POINT_cmp( + group.as_ptr(), + self.as_ptr(), + other.as_ptr(), + ctx.as_ptr(), + ))?; + Ok(res == 0) + } + } + + /// Place affine coordinates of a curve over a prime field in the provided + /// `x` and `y` `BigNum`s + /// + /// OpenSSL documentation at [`EC_POINT_get_affine_coordinates_GFp`] + /// + /// [`EC_POINT_get_affine_coordinates_GFp`]: https://www.openssl.org/docs/man1.1.0/crypto/EC_POINT_get_affine_coordinates_GFp.html + pub fn affine_coordinates_gfp( + &self, + group: &EcGroupRef, + x: &mut BigNumRef, + y: &mut BigNumRef, + ctx: &mut BigNumContextRef, + ) -> Result<(), ErrorStack> { + unsafe { + cvt(ffi::EC_POINT_get_affine_coordinates_GFp( + group.as_ptr(), + self.as_ptr(), + x.as_ptr(), + y.as_ptr(), + ctx.as_ptr(), + )) + .map(|_| ()) + } + } +} + +impl EcPoint { + /// Creates a new point on the specified curve. + /// + /// OpenSSL documentation at [`EC_POINT_new`] + /// + /// [`EC_POINT_new`]: https://www.openssl.org/docs/man1.1.0/crypto/EC_POINT_new.html + pub fn new(group: &EcGroupRef) -> Result { + unsafe { cvt_p(ffi::EC_POINT_new(group.as_ptr())).map(|p| EcPoint::from_ptr(p)) } + } + + /// Creates point from a binary representation + /// + /// OpenSSL documentation at [`EC_POINT_oct2point`] + /// + /// [`EC_POINT_oct2point`]: https://www.openssl.org/docs/man1.1.0/crypto/EC_POINT_oct2point.html + pub fn from_bytes( + group: &EcGroupRef, + buf: &[u8], + ctx: &mut BigNumContextRef, + ) -> Result { + let point = EcPoint::new(group)?; + unsafe { + cvt(ffi::EC_POINT_oct2point( + group.as_ptr(), + point.as_ptr(), + buf.as_ptr(), + buf.len(), + ctx.as_ptr(), + ))?; + } + Ok(point) + } +} + +generic_foreign_type_and_impl_send_sync! { + type CType = ffi::EC_KEY; + fn drop = ffi::EC_KEY_free; + + /// Public and optional Private key on the given curve + /// + /// OpenSSL documentation at [`EC_KEY_new`] + /// + /// [`EC_KEY_new`]: https://www.openssl.org/docs/man1.1.0/crypto/EC_KEY_new.html + pub struct EcKey; + + /// Reference to [`EcKey`] + /// + /// [`EcKey`]: struct.EcKey.html + pub struct EcKeyRef; +} + +impl EcKeyRef +where + T: HasPrivate, +{ + private_key_to_pem! { + /// Serializes the private key to a PEM-encoded ECPrivateKey structure. + /// + /// The output will have a header of `-----BEGIN EC PRIVATE KEY-----`. + /// + /// This corresponds to [`PEM_write_bio_ECPrivateKey`]. + /// + /// [`PEM_write_bio_ECPrivateKey`]: https://www.openssl.org/docs/man1.1.0/crypto/PEM_write_bio_ECPrivateKey.html + private_key_to_pem, + /// Serializes the private key to a PEM-encoded encrypted ECPrivateKey structure. + /// + /// The output will have a header of `-----BEGIN EC PRIVATE KEY-----`. + /// + /// This corresponds to [`PEM_write_bio_ECPrivateKey`]. + /// + /// [`PEM_write_bio_ECPrivateKey`]: https://www.openssl.org/docs/man1.1.0/crypto/PEM_write_bio_ECPrivateKey.html + private_key_to_pem_passphrase, + ffi::PEM_write_bio_ECPrivateKey + } + + to_der! { + /// Serializes the private key into a DER-encoded ECPrivateKey structure. + /// + /// This corresponds to [`i2d_ECPrivateKey`]. + /// + /// [`i2d_ECPrivateKey`]: https://www.openssl.org/docs/man1.0.2/crypto/d2i_ECPrivate_key.html + private_key_to_der, + ffi::i2d_ECPrivateKey + } + + /// Return [`EcPoint`] associated with the private key + /// + /// OpenSSL documentation at [`EC_KEY_get0_private_key`] + /// + /// [`EC_KEY_get0_private_key`]: https://www.openssl.org/docs/man1.1.0/crypto/EC_KEY_get0_private_key.html + pub fn private_key(&self) -> &BigNumRef { + unsafe { + let ptr = ffi::EC_KEY_get0_private_key(self.as_ptr()); + BigNumRef::from_ptr(ptr as *mut _) + } + } +} + +impl EcKeyRef +where + T: HasPublic, +{ + /// Returns the public key. + /// + /// OpenSSL documentation at [`EC_KEY_get0_public_key`] + /// + /// [`EC_KEY_get0_public_key`]: https://www.openssl.org/docs/man1.1.0/crypto/EC_KEY_get0_public_key.html + pub fn public_key(&self) -> &EcPointRef { + unsafe { + let ptr = ffi::EC_KEY_get0_public_key(self.as_ptr()); + EcPointRef::from_ptr(ptr as *mut _) + } + } + + to_pem! { + /// Serialies the public key into a PEM-encoded SubjectPublicKeyInfo structure. + /// + /// The output will have a header of `-----BEGIN PUBLIC KEY-----`. + /// + /// This corresponds to [`PEM_write_bio_EC_PUBKEY`]. + /// + /// [`PEM_write_bio_EC_PUBKEY`]: https://www.openssl.org/docs/man1.1.0/crypto/PEM_write_bio_EC_PUBKEY.html + public_key_to_pem, + ffi::PEM_write_bio_EC_PUBKEY + } + + to_der! { + /// Serializes the public key into a DER-encoded SubjectPublicKeyInfo structure. + /// + /// This corresponds to [`i2d_EC_PUBKEY`]. + /// + /// [`i2d_EC_PUBKEY`]: https://www.openssl.org/docs/man1.1.0/crypto/i2d_EC_PUBKEY.html + public_key_to_der, + ffi::i2d_EC_PUBKEY + } +} + +impl EcKeyRef +where + T: HasParams, +{ + /// Return [`EcGroup`] of the `EcKey` + /// + /// OpenSSL documentation at [`EC_KEY_get0_group`] + /// + /// [`EC_KEY_get0_group`]: https://www.openssl.org/docs/man1.1.0/crypto/EC_KEY_get0_group.html + pub fn group(&self) -> &EcGroupRef { + unsafe { + let ptr = ffi::EC_KEY_get0_group(self.as_ptr()); + EcGroupRef::from_ptr(ptr as *mut _) + } + } + + /// Checks the key for validity. + /// + /// OpenSSL documenation at [`EC_KEY_check_key`] + /// + /// [`EC_KEY_check_key`]: https://www.openssl.org/docs/man1.1.0/crypto/EC_KEY_check_key.html + pub fn check_key(&self) -> Result<(), ErrorStack> { + unsafe { cvt(ffi::EC_KEY_check_key(self.as_ptr())).map(|_| ()) } + } +} + +impl ToOwned for EcKeyRef { + type Owned = EcKey; + + fn to_owned(&self) -> EcKey { + unsafe { + let r = ffi::EC_KEY_up_ref(self.as_ptr()); + assert!(r == 1); + EcKey::from_ptr(self.as_ptr()) + } + } +} + +impl EcKey { + /// Constructs an `EcKey` corresponding to a known curve. + /// + /// It will not have an associated public or private key. This kind of key is primarily useful + /// to be provided to the `set_tmp_ecdh` methods on `Ssl` and `SslContextBuilder`. + /// + /// OpenSSL documenation at [`EC_KEY_new_by_curve_name`] + /// + /// [`EC_KEY_new_by_curve_name`]: https://www.openssl.org/docs/man1.1.0/crypto/EC_KEY_new_by_curve_name.html + pub fn from_curve_name(nid: Nid) -> Result, ErrorStack> { + unsafe { + init(); + cvt_p(ffi::EC_KEY_new_by_curve_name(nid.as_raw())).map(|p| EcKey::from_ptr(p)) + } + } + + /// Constructs an `EcKey` corresponding to a curve. + /// + /// This corresponds to [`EC_KEY_set_group`]. + /// + /// [`EC_KEY_set_group`]: https://www.openssl.org/docs/man1.1.0/crypto/EC_KEY_new.html + pub fn from_group(group: &EcGroupRef) -> Result, ErrorStack> { + unsafe { + cvt_p(ffi::EC_KEY_new()) + .map(|p| EcKey::from_ptr(p)) + .and_then(|key| { + cvt(ffi::EC_KEY_set_group(key.as_ptr(), group.as_ptr())).map(|_| key) + }) + } + } +} + +impl EcKey { + /// Constructs an `EcKey` from the specified group with the associated `EcPoint`, public_key. + /// + /// This will only have the associated public_key. + /// + /// # Example + /// + /// ```no_run + /// use boring::bn::BigNumContext; + /// use boring::ec::*; + /// use boring::nid::Nid; + /// use boring::pkey::PKey; + /// + /// // get bytes from somewhere, i.e. this will not produce a valid key + /// let public_key: Vec = vec![]; + /// + /// // create an EcKey from the binary form of a EcPoint + /// let group = EcGroup::from_curve_name(Nid::SECP256K1).unwrap(); + /// let mut ctx = BigNumContext::new().unwrap(); + /// let point = EcPoint::from_bytes(&group, &public_key, &mut ctx).unwrap(); + /// let key = EcKey::from_public_key(&group, &point); + /// ``` + pub fn from_public_key( + group: &EcGroupRef, + public_key: &EcPointRef, + ) -> Result, ErrorStack> { + unsafe { + cvt_p(ffi::EC_KEY_new()) + .map(|p| EcKey::from_ptr(p)) + .and_then(|key| { + cvt(ffi::EC_KEY_set_group(key.as_ptr(), group.as_ptr())).map(|_| key) + }) + .and_then(|key| { + cvt(ffi::EC_KEY_set_public_key( + key.as_ptr(), + public_key.as_ptr(), + )) + .map(|_| key) + }) + } + } + + /// Constructs a public key from its affine coordinates. + pub fn from_public_key_affine_coordinates( + group: &EcGroupRef, + x: &BigNumRef, + y: &BigNumRef, + ) -> Result, ErrorStack> { + unsafe { + cvt_p(ffi::EC_KEY_new()) + .map(|p| EcKey::from_ptr(p)) + .and_then(|key| { + cvt(ffi::EC_KEY_set_group(key.as_ptr(), group.as_ptr())).map(|_| key) + }) + .and_then(|key| { + cvt(ffi::EC_KEY_set_public_key_affine_coordinates( + key.as_ptr(), + x.as_ptr(), + y.as_ptr(), + )) + .map(|_| key) + }) + } + } + + from_pem! { + /// Decodes a PEM-encoded SubjectPublicKeyInfo structure containing a EC key. + /// + /// The input should have a header of `-----BEGIN PUBLIC KEY-----`. + /// + /// This corresponds to [`PEM_read_bio_EC_PUBKEY`]. + /// + /// [`PEM_read_bio_EC_PUBKEY`]: https://www.openssl.org/docs/man1.1.0/crypto/PEM_read_bio_EC_PUBKEY.html + public_key_from_pem, + EcKey, + ffi::PEM_read_bio_EC_PUBKEY + } + + from_der! { + /// Decodes a DER-encoded SubjectPublicKeyInfo structure containing a EC key. + /// + /// This corresponds to [`d2i_EC_PUBKEY`]. + /// + /// [`d2i_EC_PUBKEY`]: https://www.openssl.org/docs/man1.1.0/crypto/d2i_EC_PUBKEY.html + public_key_from_der, + EcKey, + ffi::d2i_EC_PUBKEY, + ::libc::c_long + } +} + +impl EcKey { + /// Generates a new public/private key pair on the specified curve. + pub fn generate(group: &EcGroupRef) -> Result, ErrorStack> { + unsafe { + cvt_p(ffi::EC_KEY_new()) + .map(|p| EcKey::from_ptr(p)) + .and_then(|key| { + cvt(ffi::EC_KEY_set_group(key.as_ptr(), group.as_ptr())).map(|_| key) + }) + .and_then(|key| cvt(ffi::EC_KEY_generate_key(key.as_ptr())).map(|_| key)) + } + } + + /// Constructs an public/private key pair given a curve, a private key and a public key point. + pub fn from_private_components( + group: &EcGroupRef, + private_number: &BigNumRef, + public_key: &EcPointRef, + ) -> Result, ErrorStack> { + unsafe { + cvt_p(ffi::EC_KEY_new()) + .map(|p| EcKey::from_ptr(p)) + .and_then(|key| { + cvt(ffi::EC_KEY_set_group(key.as_ptr(), group.as_ptr())).map(|_| key) + }) + .and_then(|key| { + cvt(ffi::EC_KEY_set_private_key( + key.as_ptr(), + private_number.as_ptr(), + )) + .map(|_| key) + }) + .and_then(|key| { + cvt(ffi::EC_KEY_set_public_key( + key.as_ptr(), + public_key.as_ptr(), + )) + .map(|_| key) + }) + } + } + + private_key_from_pem! { + /// Deserializes a private key from a PEM-encoded ECPrivateKey structure. + /// + /// The input should have a header of `-----BEGIN EC PRIVATE KEY-----`. + /// + /// This corresponds to `PEM_read_bio_ECPrivateKey`. + private_key_from_pem, + + /// Deserializes a private key from a PEM-encoded encrypted ECPrivateKey structure. + /// + /// The input should have a header of `-----BEGIN EC PRIVATE KEY-----`. + /// + /// This corresponds to `PEM_read_bio_ECPrivateKey`. + private_key_from_pem_passphrase, + + /// Deserializes a private key from a PEM-encoded encrypted ECPrivateKey structure. + /// + /// The callback should fill the password into the provided buffer and return its length. + /// + /// The input should have a header of `-----BEGIN EC PRIVATE KEY-----`. + /// + /// This corresponds to `PEM_read_bio_ECPrivateKey`. + private_key_from_pem_callback, + EcKey, + ffi::PEM_read_bio_ECPrivateKey + } + + from_der! { + /// Decodes a DER-encoded elliptic curve private key structure. + /// + /// This corresponds to [`d2i_ECPrivateKey`]. + /// + /// [`d2i_ECPrivateKey`]: https://www.openssl.org/docs/man1.0.2/crypto/d2i_ECPrivate_key.html + private_key_from_der, + EcKey, + ffi::d2i_ECPrivateKey, + ::libc::c_long + } +} + +impl Clone for EcKey { + fn clone(&self) -> EcKey { + (**self).to_owned() + } +} + +impl fmt::Debug for EcKey { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "EcKey") + } +} + +#[cfg(test)] +mod test { + use hex::FromHex; + + use super::*; + use crate::bn::{BigNum, BigNumContext}; + use crate::nid::Nid; + + #[test] + fn key_new_by_curve_name() { + EcKey::from_curve_name(Nid::X9_62_PRIME256V1).unwrap(); + } + + #[test] + fn generate() { + let group = EcGroup::from_curve_name(Nid::X9_62_PRIME256V1).unwrap(); + EcKey::generate(&group).unwrap(); + } + + #[test] + fn cofactor() { + let group = EcGroup::from_curve_name(Nid::X9_62_PRIME256V1).unwrap(); + let mut ctx = BigNumContext::new().unwrap(); + let mut cofactor = BigNum::new().unwrap(); + group.cofactor(&mut cofactor, &mut ctx).unwrap(); + let one = BigNum::from_u32(1).unwrap(); + assert_eq!(cofactor, one); + } + + #[test] + #[allow(clippy::redundant_clone)] + fn dup() { + let group = EcGroup::from_curve_name(Nid::X9_62_PRIME256V1).unwrap(); + let key = EcKey::generate(&group).unwrap(); + drop(key.clone()); + } + + #[test] + fn point_new() { + let group = EcGroup::from_curve_name(Nid::X9_62_PRIME256V1).unwrap(); + EcPoint::new(&group).unwrap(); + } + + #[test] + fn point_bytes() { + let group = EcGroup::from_curve_name(Nid::X9_62_PRIME256V1).unwrap(); + let key = EcKey::generate(&group).unwrap(); + let point = key.public_key(); + let mut ctx = BigNumContext::new().unwrap(); + let bytes = point + .to_bytes(&group, PointConversionForm::COMPRESSED, &mut ctx) + .unwrap(); + let point2 = EcPoint::from_bytes(&group, &bytes, &mut ctx).unwrap(); + assert!(point.eq(&group, &point2, &mut ctx).unwrap()); + } + + #[test] + fn point_owned() { + let group = EcGroup::from_curve_name(Nid::X9_62_PRIME256V1).unwrap(); + let key = EcKey::generate(&group).unwrap(); + let point = key.public_key(); + let owned = point.to_owned(&group).unwrap(); + let mut ctx = BigNumContext::new().unwrap(); + assert!(owned.eq(&group, point, &mut ctx).unwrap()); + } + + #[test] + fn mul_generator() { + let group = EcGroup::from_curve_name(Nid::X9_62_PRIME256V1).unwrap(); + let key = EcKey::generate(&group).unwrap(); + let mut ctx = BigNumContext::new().unwrap(); + let mut public_key = EcPoint::new(&group).unwrap(); + public_key + .mul_generator(&group, key.private_key(), &ctx) + .unwrap(); + assert!(public_key.eq(&group, key.public_key(), &mut ctx).unwrap()); + } + + #[test] + fn generator() { + let group = EcGroup::from_curve_name(Nid::X9_62_PRIME256V1).unwrap(); + let gen = group.generator(); + let one = BigNum::from_u32(1).unwrap(); + let mut ctx = BigNumContext::new().unwrap(); + let mut ecp = EcPoint::new(&group).unwrap(); + ecp.mul_generator(&group, &one, &ctx).unwrap(); + assert!(ecp.eq(&group, gen, &mut ctx).unwrap()); + } + + #[test] + fn key_from_public_key() { + let group = EcGroup::from_curve_name(Nid::X9_62_PRIME256V1).unwrap(); + let key = EcKey::generate(&group).unwrap(); + let mut ctx = BigNumContext::new().unwrap(); + let bytes = key + .public_key() + .to_bytes(&group, PointConversionForm::COMPRESSED, &mut ctx) + .unwrap(); + + drop(key); + let public_key = EcPoint::from_bytes(&group, &bytes, &mut ctx).unwrap(); + let ec_key = EcKey::from_public_key(&group, &public_key).unwrap(); + assert!(ec_key.check_key().is_ok()); + } + + #[test] + fn key_from_private_components() { + let group = EcGroup::from_curve_name(Nid::X9_62_PRIME256V1).unwrap(); + let key = EcKey::generate(&group).unwrap(); + + let dup_key = + EcKey::from_private_components(&group, key.private_key(), key.public_key()).unwrap(); + dup_key.check_key().unwrap(); + + assert!(key.private_key() == dup_key.private_key()); + } + + #[test] + fn key_from_affine_coordinates() { + let group = EcGroup::from_curve_name(Nid::X9_62_PRIME256V1).unwrap(); + let x = Vec::from_hex("30a0424cd21c2944838a2d75c92b37e76ea20d9f00893a3b4eee8a3c0aafec3e") + .unwrap(); + let y = Vec::from_hex("e04b65e92456d9888b52b379bdfbd51ee869ef1f0fc65b6659695b6cce081723") + .unwrap(); + + let xbn = BigNum::from_slice(&x).unwrap(); + let ybn = BigNum::from_slice(&y).unwrap(); + + let ec_key = EcKey::from_public_key_affine_coordinates(&group, &xbn, &ybn).unwrap(); + assert!(ec_key.check_key().is_ok()); + } + + #[test] + fn get_affine_coordinates() { + let group = EcGroup::from_curve_name(Nid::X9_62_PRIME256V1).unwrap(); + let x = Vec::from_hex("30a0424cd21c2944838a2d75c92b37e76ea20d9f00893a3b4eee8a3c0aafec3e") + .unwrap(); + let y = Vec::from_hex("e04b65e92456d9888b52b379bdfbd51ee869ef1f0fc65b6659695b6cce081723") + .unwrap(); + + let xbn = BigNum::from_slice(&x).unwrap(); + let ybn = BigNum::from_slice(&y).unwrap(); + + let ec_key = EcKey::from_public_key_affine_coordinates(&group, &xbn, &ybn).unwrap(); + + let mut xbn2 = BigNum::new().unwrap(); + let mut ybn2 = BigNum::new().unwrap(); + let mut ctx = BigNumContext::new().unwrap(); + let ec_key_pk = ec_key.public_key(); + ec_key_pk + .affine_coordinates_gfp(&group, &mut xbn2, &mut ybn2, &mut ctx) + .unwrap(); + assert_eq!(xbn2, xbn); + assert_eq!(ybn2, ybn); + } +} diff --git a/crates/boring/src/ecdsa.rs b/crates/boring/src/ecdsa.rs new file mode 100644 index 000000000..0d333003c --- /dev/null +++ b/crates/boring/src/ecdsa.rs @@ -0,0 +1,135 @@ +//! Low level Elliptic Curve Digital Signature Algorithm (ECDSA) functions. + +use crate::ffi; +use foreign_types::{ForeignType, ForeignTypeRef}; +use libc::{c_int, size_t}; +use std::mem; +use std::ptr; + +use crate::bn::{BigNum, BigNumRef}; +use crate::ec::EcKeyRef; +use crate::error::ErrorStack; +use crate::pkey::{HasPrivate, HasPublic}; +use crate::{cvt_n, cvt_p}; + +foreign_type_and_impl_send_sync! { + type CType = ffi::ECDSA_SIG; + fn drop = ffi::ECDSA_SIG_free; + + /// A low level interface to ECDSA + /// + /// OpenSSL documentation at [`ECDSA_sign`] + /// + /// [`ECDSA_sign`]: https://www.openssl.org/docs/man1.1.0/crypto/ECDSA_sign.html + pub struct EcdsaSig; +} + +impl EcdsaSig { + /// Computes a digital signature of the hash value `data` using the private EC key eckey. + /// + /// OpenSSL documentation at [`ECDSA_do_sign`] + /// + /// [`ECDSA_do_sign`]: https://www.openssl.org/docs/man1.1.0/crypto/ECDSA_do_sign.html + pub fn sign(data: &[u8], eckey: &EcKeyRef) -> Result + where + T: HasPrivate, + { + unsafe { + assert!(data.len() <= c_int::max_value() as usize); + let sig = cvt_p(ffi::ECDSA_do_sign( + data.as_ptr(), + data.len() as size_t, + eckey.as_ptr(), + ))?; + Ok(EcdsaSig::from_ptr(sig as *mut _)) + } + } + + /// Returns a new `EcdsaSig` by setting the `r` and `s` values associated with a + /// ECDSA signature. + /// + /// OpenSSL documentation at [`ECDSA_SIG_set0`] + /// + /// [`ECDSA_SIG_set0`]: https://www.openssl.org/docs/man1.1.0/crypto/ECDSA_SIG_set0.html + pub fn from_private_components(r: BigNum, s: BigNum) -> Result { + unsafe { + let sig = cvt_p(ffi::ECDSA_SIG_new())?; + ECDSA_SIG_set0(sig, r.as_ptr(), s.as_ptr()); + mem::forget((r, s)); + Ok(EcdsaSig::from_ptr(sig as *mut _)) + } + } + + from_der! { + /// Decodes a DER-encoded ECDSA signature. + /// + /// This corresponds to [`d2i_ECDSA_SIG`]. + /// + /// [`d2i_ECDSA_SIG`]: https://www.openssl.org/docs/man1.1.0/crypto/d2i_ECDSA_SIG.html + from_der, + EcdsaSig, + ffi::d2i_ECDSA_SIG, + ::libc::c_long + } +} + +impl EcdsaSigRef { + to_der! { + /// Serializes the ECDSA signature into a DER-encoded ECDSASignature structure. + /// + /// This corresponds to [`i2d_ECDSA_SIG`]. + /// + /// [`i2d_ECDSA_SIG`]: https://www.openssl.org/docs/man1.1.0/crypto/i2d_ECDSA_SIG.html + to_der, + ffi::i2d_ECDSA_SIG + } + + /// Verifies if the signature is a valid ECDSA signature using the given public key. + /// + /// OpenSSL documentation at [`ECDSA_do_verify`] + /// + /// [`ECDSA_do_verify`]: https://www.openssl.org/docs/man1.1.0/crypto/ECDSA_do_verify.html + pub fn verify(&self, data: &[u8], eckey: &EcKeyRef) -> Result + where + T: HasPublic, + { + unsafe { + assert!(data.len() <= c_int::max_value() as usize); + cvt_n(ffi::ECDSA_do_verify( + data.as_ptr(), + data.len() as size_t, + self.as_ptr(), + eckey.as_ptr(), + )) + .map(|x| x == 1) + } + } + + /// Returns internal component: `r` of an `EcdsaSig`. (See X9.62 or FIPS 186-2) + /// + /// OpenSSL documentation at [`ECDSA_SIG_get0`] + /// + /// [`ECDSA_SIG_get0`]: https://www.openssl.org/docs/man1.1.0/crypto/ECDSA_SIG_get0.html + pub fn r(&self) -> &BigNumRef { + unsafe { + let mut r = ptr::null(); + ECDSA_SIG_get0(self.as_ptr(), &mut r, ptr::null_mut()); + BigNumRef::from_ptr(r as *mut _) + } + } + + /// Returns internal components: `s` of an `EcdsaSig`. (See X9.62 or FIPS 186-2) + /// + /// OpenSSL documentation at [`ECDSA_SIG_get0`] + /// + /// [`ECDSA_SIG_get0`]: https://www.openssl.org/docs/man1.1.0/crypto/ECDSA_SIG_get0.html + pub fn s(&self) -> &BigNumRef { + unsafe { + let mut s = ptr::null(); + ECDSA_SIG_get0(self.as_ptr(), ptr::null_mut(), &mut s); + BigNumRef::from_ptr(s as *mut _) + } + } +} + +use crate::ffi::{ECDSA_SIG_get0, ECDSA_SIG_set0}; diff --git a/crates/boring/src/error.rs b/crates/boring/src/error.rs new file mode 100644 index 000000000..fdc3ba9ef --- /dev/null +++ b/crates/boring/src/error.rs @@ -0,0 +1,266 @@ +//! Errors returned by OpenSSL library. +//! +//! OpenSSL errors are stored in an `ErrorStack`. Most methods in the crate +//! returns a `Result` type. +//! +//! # Examples +//! +//! ``` +//! use boring::error::ErrorStack; +//! use boring::bn::BigNum; +//! +//! let an_error = BigNum::from_dec_str("Cannot parse letters"); +//! match an_error { +//! Ok(_) => (), +//! Err(e) => println!("Parsing Error: {:?}", e), +//! } +//! ``` +use libc::{c_char, c_uint}; +use std::borrow::Cow; +use std::error; +use std::ffi::CStr; +use std::fmt; +use std::io; +use std::ptr; +use std::str; + +use crate::ffi; + +/// Collection of [`Error`]s from OpenSSL. +/// +/// [`Error`]: struct.Error.html +#[derive(Debug, Clone)] +pub struct ErrorStack(Vec); + +impl ErrorStack { + /// Returns the contents of the OpenSSL error stack. + pub fn get() -> ErrorStack { + let mut vec = vec![]; + while let Some(err) = Error::get() { + vec.push(err); + } + ErrorStack(vec) + } + + /// Pushes the errors back onto the OpenSSL error stack. + pub fn put(&self) { + for error in self.errors() { + error.put(); + } + } +} + +impl ErrorStack { + /// Returns the errors in the stack. + pub fn errors(&self) -> &[Error] { + &self.0 + } +} + +impl fmt::Display for ErrorStack { + fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { + if self.0.is_empty() { + return fmt.write_str("unknown BoringSSL error"); + } + + let mut first = true; + for err in &self.0 { + if !first { + fmt.write_str(" ")?; + } + first = false; + write!(fmt, "[{}]", err.reason().unwrap_or("unknown reason"))?; + } + Ok(()) + } +} + +impl error::Error for ErrorStack {} + +impl From for io::Error { + fn from(e: ErrorStack) -> io::Error { + io::Error::new(io::ErrorKind::Other, e) + } +} + +impl From for fmt::Error { + fn from(_: ErrorStack) -> fmt::Error { + fmt::Error + } +} + +/// An error reported from OpenSSL. +#[derive(Clone)] +pub struct Error { + code: c_uint, + file: *const c_char, + line: c_uint, + data: Option>, +} + +unsafe impl Sync for Error {} +unsafe impl Send for Error {} + +impl Error { + /// Returns the first error on the OpenSSL error stack. + pub fn get() -> Option { + unsafe { + ffi::init(); + + let mut file = ptr::null(); + let mut line = 0; + let mut data = ptr::null(); + let mut flags = 0; + match ffi::ERR_get_error_line_data(&mut file, &mut line, &mut data, &mut flags) { + 0 => None, + code => { + // The memory referenced by data is only valid until that slot is overwritten + // in the error stack, so we'll need to copy it off if it's dynamic + let data = if flags & ffi::ERR_FLAG_STRING != 0 { + let bytes = CStr::from_ptr(data as *const _).to_bytes(); + let data = str::from_utf8(bytes).unwrap(); + let data = Cow::Owned(data.to_string()); + Some(data) + } else { + None + }; + Some(Error { + code, + file, + line: line as c_uint, + data, + }) + } + } + } + } + + /// Pushes the error back onto the OpenSSL error stack. + pub fn put(&self) { + unsafe { + ffi::ERR_put_error( + ffi::ERR_GET_LIB(self.code), + ffi::ERR_GET_FUNC(self.code), + ffi::ERR_GET_REASON(self.code), + self.file, + self.line, + ); + let ptr = match self.data { + Some(Cow::Borrowed(data)) => Some(data.as_ptr() as *mut c_char), + Some(Cow::Owned(ref data)) => { + let ptr = ffi::OPENSSL_malloc((data.len() + 1) as _) as *mut c_char; + if ptr.is_null() { + None + } else { + ptr::copy_nonoverlapping(data.as_ptr(), ptr as *mut u8, data.len()); + *ptr.add(data.len()) = 0; + Some(ptr) + } + } + None => None, + }; + if let Some(ptr) = ptr { + ffi::ERR_add_error_data(1, ptr); + } + } + } + + /// Returns the raw OpenSSL error code for this error. + pub fn code(&self) -> c_uint { + self.code + } + + /// Returns the name of the library reporting the error, if available. + pub fn library(&self) -> Option<&'static str> { + unsafe { + let cstr = ffi::ERR_lib_error_string(self.code); + if cstr.is_null() { + return None; + } + let bytes = CStr::from_ptr(cstr as *const _).to_bytes(); + Some(str::from_utf8(bytes).unwrap()) + } + } + + /// Returns the name of the function reporting the error. + pub fn function(&self) -> Option<&'static str> { + unsafe { + let cstr = ffi::ERR_func_error_string(self.code); + if cstr.is_null() { + return None; + } + let bytes = CStr::from_ptr(cstr as *const _).to_bytes(); + Some(str::from_utf8(bytes).unwrap()) + } + } + + /// Returns the reason for the error. + pub fn reason(&self) -> Option<&'static str> { + unsafe { + let cstr = ffi::ERR_reason_error_string(self.code); + if cstr.is_null() { + return None; + } + let bytes = CStr::from_ptr(cstr as *const _).to_bytes(); + Some(str::from_utf8(bytes).unwrap()) + } + } + + /// Returns the name of the source file which encountered the error. + pub fn file(&self) -> &'static str { + unsafe { + assert!(!self.file.is_null()); + let bytes = CStr::from_ptr(self.file as *const _).to_bytes(); + str::from_utf8(bytes).unwrap() + } + } + + /// Returns the line in the source file which encountered the error. + #[allow(clippy::unnecessary_cast)] + pub fn line(&self) -> u32 { + self.line as u32 + } + + /// Returns additional data describing the error. + #[allow(clippy::option_as_ref_deref)] + pub fn data(&self) -> Option<&str> { + self.data.as_ref().map(|s| &**s) + } +} + +impl fmt::Debug for Error { + fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { + let mut builder = fmt.debug_struct("Error"); + builder.field("code", &self.code()); + if let Some(library) = self.library() { + builder.field("library", &library); + } + if let Some(function) = self.function() { + builder.field("function", &function); + } + if let Some(reason) = self.reason() { + builder.field("reason", &reason); + } + builder.field("file", &self.file()); + builder.field("line", &self.line()); + if let Some(data) = self.data() { + builder.field("data", &data); + } + builder.finish() + } +} + +impl fmt::Display for Error { + fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { + write!( + fmt, + "{}\n\nCode: {:08X}\nLoc: {}:{}", + self.reason().unwrap_or("unknown TLS error"), + self.code(), + self.file(), + self.line() + ) + } +} + +impl error::Error for Error {} diff --git a/crates/boring/src/ex_data.rs b/crates/boring/src/ex_data.rs new file mode 100644 index 000000000..d4f002129 --- /dev/null +++ b/crates/boring/src/ex_data.rs @@ -0,0 +1,32 @@ +use libc::c_int; +use std::marker::PhantomData; + +/// A slot in a type's "extra data" structure. +/// +/// It is parameterized over the type containing the extra data as well as the +/// type of the data in the slot. +pub struct Index(c_int, PhantomData<(T, U)>); + +impl Copy for Index {} + +impl Clone for Index { + fn clone(&self) -> Index { + *self + } +} + +impl Index { + /// Creates an `Index` from a raw integer index. + /// + /// # Safety + /// + /// The caller must ensure that the index correctly maps to a `U` value stored in a `T`. + pub unsafe fn from_raw(idx: c_int) -> Index { + Index(idx, PhantomData) + } + + #[allow(clippy::trivially_copy_pass_by_ref)] + pub fn as_raw(&self) -> c_int { + self.0 + } +} diff --git a/crates/boring/src/fips.rs b/crates/boring/src/fips.rs new file mode 100644 index 000000000..e578ae751 --- /dev/null +++ b/crates/boring/src/fips.rs @@ -0,0 +1,21 @@ +//! FIPS 140-2 support. +//! +//! See [OpenSSL's documentation] for details. +//! +//! [OpenSSL's documentation]: https://www.openssl.org/docs/fips/UserGuide-2.0.pdf +use crate::ffi; + +/// Determines if the library is running in the FIPS 140-2 mode of operation. +/// +/// This corresponds to `FIPS_mode`. +pub fn enabled() -> bool { + unsafe { ffi::FIPS_mode() != 0 } +} + +#[test] +fn is_enabled() { + #[cfg(any(feature = "fips", feature = "fips-link-precompiled"))] + assert!(enabled()); + #[cfg(not(any(feature = "fips", feature = "fips-link-precompiled")))] + assert!(!enabled()); +} diff --git a/crates/boring/src/hash.rs b/crates/boring/src/hash.rs new file mode 100644 index 000000000..5690a05a7 --- /dev/null +++ b/crates/boring/src/hash.rs @@ -0,0 +1,496 @@ +use crate::ffi; +use std::convert::TryInto; +use std::fmt; +use std::io; +use std::io::prelude::*; +use std::ops::{Deref, DerefMut}; +use std::ptr; + +use crate::error::ErrorStack; +use crate::ffi::{EVP_MD_CTX_free, EVP_MD_CTX_new}; +use crate::nid::Nid; +use crate::{cvt, cvt_p}; + +#[derive(Copy, Clone, PartialEq, Eq)] +pub struct MessageDigest(*const ffi::EVP_MD); + +impl MessageDigest { + /// Creates a `MessageDigest` from a raw OpenSSL pointer. + /// + /// # Safety + /// + /// The caller must ensure the pointer is valid. + pub unsafe fn from_ptr(x: *const ffi::EVP_MD) -> Self { + MessageDigest(x) + } + + /// Returns the `MessageDigest` corresponding to an `Nid`. + /// + /// This corresponds to [`EVP_get_digestbynid`]. + /// + /// [`EVP_get_digestbynid`]: https://www.openssl.org/docs/man1.1.0/crypto/EVP_DigestInit.html + pub fn from_nid(type_: Nid) -> Option { + unsafe { + let ptr = ffi::EVP_get_digestbynid(type_.as_raw()); + if ptr.is_null() { + None + } else { + Some(MessageDigest(ptr)) + } + } + } + + pub fn md5() -> MessageDigest { + unsafe { MessageDigest(ffi::EVP_md5()) } + } + + pub fn sha1() -> MessageDigest { + unsafe { MessageDigest(ffi::EVP_sha1()) } + } + + pub fn sha224() -> MessageDigest { + unsafe { MessageDigest(ffi::EVP_sha224()) } + } + + pub fn sha256() -> MessageDigest { + unsafe { MessageDigest(ffi::EVP_sha256()) } + } + + pub fn sha384() -> MessageDigest { + unsafe { MessageDigest(ffi::EVP_sha384()) } + } + + pub fn sha512() -> MessageDigest { + unsafe { MessageDigest(ffi::EVP_sha512()) } + } + + pub fn sha512_256() -> MessageDigest { + unsafe { MessageDigest(ffi::EVP_sha512_256()) } + } + + #[allow(clippy::trivially_copy_pass_by_ref)] + pub fn as_ptr(&self) -> *const ffi::EVP_MD { + self.0 + } + + /// The size of the digest in bytes. + #[allow(clippy::trivially_copy_pass_by_ref)] + pub fn size(&self) -> usize { + unsafe { ffi::EVP_MD_size(self.0) } + } + + /// The name of the digest. + #[allow(clippy::trivially_copy_pass_by_ref)] + pub fn type_(&self) -> Nid { + Nid::from_raw(unsafe { ffi::EVP_MD_type(self.0) }) + } +} + +unsafe impl Sync for MessageDigest {} +unsafe impl Send for MessageDigest {} + +#[derive(PartialEq, Copy, Clone)] +enum State { + Reset, + Updated, + Finalized, +} + +use self::State::*; + +/// Provides message digest (hash) computation. +/// +/// # Examples +/// +/// Calculate a hash in one go: +/// +/// ``` +/// use boring::hash::{hash, MessageDigest}; +/// +/// let data = b"\x42\xF4\x97\xE0"; +/// let spec = b"\x7c\x43\x0f\x17\x8a\xef\xdf\x14\x87\xfe\xe7\x14\x4e\x96\x41\xe2"; +/// let res = hash(MessageDigest::md5(), data).unwrap(); +/// assert_eq!(&*res, spec); +/// ``` +/// +/// Supply the input in chunks: +/// +/// ``` +/// use boring::hash::{Hasher, MessageDigest}; +/// +/// let data = [b"\x42\xF4", b"\x97\xE0"]; +/// let spec = b"\x7c\x43\x0f\x17\x8a\xef\xdf\x14\x87\xfe\xe7\x14\x4e\x96\x41\xe2"; +/// let mut h = Hasher::new(MessageDigest::md5()).unwrap(); +/// h.update(data[0]).unwrap(); +/// h.update(data[1]).unwrap(); +/// let res = h.finish().unwrap(); +/// assert_eq!(&*res, spec); +/// ``` +/// +/// # Warning +/// +/// Don't actually use MD5 and SHA-1 hashes, they're not secure anymore. +/// +/// Don't ever hash passwords, use the functions in the `pkcs5` module or bcrypt/scrypt instead. +/// +/// For extendable output functions (XOFs, i.e. SHAKE128/SHAKE256), you must use finish_xof instead +/// of finish and provide a buf to store the hash. The hash will be as long as the buf. +pub struct Hasher { + ctx: *mut ffi::EVP_MD_CTX, + md: *const ffi::EVP_MD, + type_: MessageDigest, + state: State, +} + +unsafe impl Sync for Hasher {} +unsafe impl Send for Hasher {} + +impl Hasher { + /// Creates a new `Hasher` with the specified hash type. + pub fn new(ty: MessageDigest) -> Result { + ffi::init(); + + let ctx = unsafe { cvt_p(EVP_MD_CTX_new())? }; + + let mut h = Hasher { + ctx, + md: ty.as_ptr(), + type_: ty, + state: Finalized, + }; + h.init()?; + Ok(h) + } + + fn init(&mut self) -> Result<(), ErrorStack> { + match self.state { + Reset => return Ok(()), + Updated => { + self.finish()?; + } + Finalized => (), + } + unsafe { + cvt(ffi::EVP_DigestInit_ex(self.ctx, self.md, ptr::null_mut()))?; + } + self.state = Reset; + Ok(()) + } + + /// Feeds data into the hasher. + pub fn update(&mut self, data: &[u8]) -> Result<(), ErrorStack> { + if self.state == Finalized { + self.init()?; + } + unsafe { + cvt(ffi::EVP_DigestUpdate( + self.ctx, + data.as_ptr() as *mut _, + data.len(), + ))?; + } + self.state = Updated; + Ok(()) + } + + /// Returns the hash of the data written and resets the non-XOF hasher. + pub fn finish(&mut self) -> Result { + if self.state == Finalized { + self.init()?; + } + unsafe { + let mut len = ffi::EVP_MAX_MD_SIZE.try_into().unwrap(); + let mut buf = [0; ffi::EVP_MAX_MD_SIZE as usize]; + cvt(ffi::EVP_DigestFinal_ex( + self.ctx, + buf.as_mut_ptr(), + &mut len, + ))?; + self.state = Finalized; + Ok(DigestBytes { + buf, + len: len as usize, + }) + } + } + + /// Writes the hash of the data into the supplied buf and resets the XOF hasher. + /// The hash will be as long as the buf. + pub fn finish_xof(&mut self, buf: &mut [u8]) -> Result<(), ErrorStack> { + if self.state == Finalized { + self.init()?; + } + unsafe { + cvt(ffi::EVP_DigestFinalXOF( + self.ctx, + buf.as_mut_ptr(), + buf.len(), + ))?; + self.state = Finalized; + Ok(()) + } + } +} + +impl Write for Hasher { + #[inline] + fn write(&mut self, buf: &[u8]) -> io::Result { + self.update(buf)?; + Ok(buf.len()) + } + + fn flush(&mut self) -> io::Result<()> { + Ok(()) + } +} + +impl Clone for Hasher { + fn clone(&self) -> Hasher { + let ctx = unsafe { + let ctx = EVP_MD_CTX_new(); + assert!(!ctx.is_null()); + let r = ffi::EVP_MD_CTX_copy_ex(ctx, self.ctx); + assert_eq!(r, 1); + ctx + }; + Hasher { + ctx, + md: self.md, + type_: self.type_, + state: self.state, + } + } +} + +impl Drop for Hasher { + fn drop(&mut self) { + unsafe { + if self.state != Finalized { + drop(self.finish()); + } + EVP_MD_CTX_free(self.ctx); + } + } +} + +/// The resulting bytes of a digest. +/// +/// This type derefs to a byte slice - it exists to avoid allocating memory to +/// store the digest data. +#[derive(Copy)] +pub struct DigestBytes { + pub(crate) buf: [u8; ffi::EVP_MAX_MD_SIZE as usize], + pub(crate) len: usize, +} + +impl Clone for DigestBytes { + #[inline] + fn clone(&self) -> DigestBytes { + *self + } +} + +impl Deref for DigestBytes { + type Target = [u8]; + + #[inline] + fn deref(&self) -> &[u8] { + &self.buf[..self.len] + } +} + +impl DerefMut for DigestBytes { + #[inline] + fn deref_mut(&mut self) -> &mut [u8] { + &mut self.buf[..self.len] + } +} + +impl AsRef<[u8]> for DigestBytes { + #[inline] + fn as_ref(&self) -> &[u8] { + self.deref() + } +} + +impl fmt::Debug for DigestBytes { + fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { + fmt::Debug::fmt(&**self, fmt) + } +} + +/// Computes the hash of the `data` with the non-XOF hasher `t`. +pub fn hash(t: MessageDigest, data: &[u8]) -> Result { + let mut h = Hasher::new(t)?; + h.update(data)?; + h.finish() +} + +/// Computes the hash of the `data` with the XOF hasher `t` and stores it in `buf`. +pub fn hash_xof(t: MessageDigest, data: &[u8], buf: &mut [u8]) -> Result<(), ErrorStack> { + let mut h = Hasher::new(t)?; + h.update(data)?; + h.finish_xof(buf) +} + +#[cfg(test)] +mod tests { + use hex::{self, FromHex}; + use std::io::prelude::*; + + use super::*; + + fn hash_test(hashtype: MessageDigest, hashtest: &(&str, &str)) { + let res = hash(hashtype, &Vec::from_hex(hashtest.0).unwrap()).unwrap(); + assert_eq!(hex::encode(res), hashtest.1); + } + + fn hash_recycle_test(h: &mut Hasher, hashtest: &(&str, &str)) { + h.write_all(&Vec::from_hex(hashtest.0).unwrap()).unwrap(); + let res = h.finish().unwrap(); + assert_eq!(hex::encode(res), hashtest.1); + } + + // Test vectors from http://www.nsrl.nist.gov/testdata/ + const MD5_TESTS: [(&str, &str); 13] = [ + ("", "d41d8cd98f00b204e9800998ecf8427e"), + ("7F", "83acb6e67e50e31db6ed341dd2de1595"), + ("EC9C", "0b07f0d4ca797d8ac58874f887cb0b68"), + ("FEE57A", "e0d583171eb06d56198fc0ef22173907"), + ("42F497E0", "7c430f178aefdf1487fee7144e9641e2"), + ("C53B777F1C", "75ef141d64cb37ec423da2d9d440c925"), + ("89D5B576327B", "ebbaf15eb0ed784c6faa9dc32831bf33"), + ("5D4CCE781EB190", "ce175c4b08172019f05e6b5279889f2c"), + ("81901FE94932D7B9", "cd4d2f62b8cdb3a0cf968a735a239281"), + ("C9FFDEE7788EFB4EC9", "e0841a231ab698db30c6c0f3f246c014"), + ("66AC4B7EBA95E53DC10B", "a3b3cea71910d9af56742aa0bb2fe329"), + ("A510CD18F7A56852EB0319", "577e216843dd11573574d3fb209b97d8"), + ( + "AAED18DBE8938C19ED734A8D", + "6f80fb775f27e0a4ce5c2f42fc72c5f1", + ), + ]; + + #[test] + fn test_md5() { + for test in MD5_TESTS.iter() { + hash_test(MessageDigest::md5(), test); + } + } + + #[test] + fn test_md5_recycle() { + let mut h = Hasher::new(MessageDigest::md5()).unwrap(); + for test in MD5_TESTS.iter() { + hash_recycle_test(&mut h, test); + } + } + + #[test] + fn test_finish_twice() { + let mut h = Hasher::new(MessageDigest::md5()).unwrap(); + h.write_all(&Vec::from_hex(MD5_TESTS[6].0).unwrap()) + .unwrap(); + h.finish().unwrap(); + let res = h.finish().unwrap(); + let null = hash(MessageDigest::md5(), &[]).unwrap(); + assert_eq!(&*res, &*null); + } + + #[test] + #[allow(clippy::redundant_clone)] + fn test_clone() { + let i = 7; + let inp = Vec::from_hex(MD5_TESTS[i].0).unwrap(); + assert!(inp.len() > 2); + let p = inp.len() / 2; + let h0 = Hasher::new(MessageDigest::md5()).unwrap(); + + println!("Clone a new hasher"); + let mut h1 = h0.clone(); + h1.write_all(&inp[..p]).unwrap(); + { + println!("Clone an updated hasher"); + let mut h2 = h1.clone(); + h2.write_all(&inp[p..]).unwrap(); + let res = h2.finish().unwrap(); + assert_eq!(hex::encode(res), MD5_TESTS[i].1); + } + h1.write_all(&inp[p..]).unwrap(); + let res = h1.finish().unwrap(); + assert_eq!(hex::encode(res), MD5_TESTS[i].1); + + println!("Clone a finished hasher"); + let mut h3 = h1.clone(); + h3.write_all(&Vec::from_hex(MD5_TESTS[i + 1].0).unwrap()) + .unwrap(); + let res = h3.finish().unwrap(); + assert_eq!(hex::encode(res), MD5_TESTS[i + 1].1); + } + + #[test] + fn test_sha1() { + let tests = [("616263", "a9993e364706816aba3e25717850c26c9cd0d89d")]; + + for test in tests.iter() { + hash_test(MessageDigest::sha1(), test); + } + } + + #[test] + fn test_sha224() { + let tests = [( + "616263", + "23097d223405d8228642a477bda255b32aadbce4bda0b3f7e36c9da7", + )]; + + for test in tests.iter() { + hash_test(MessageDigest::sha224(), test); + } + } + + #[test] + fn test_sha256() { + let tests = [( + "616263", + "ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad", + )]; + + for test in tests.iter() { + hash_test(MessageDigest::sha256(), test); + } + } + + #[test] + fn test_sha512() { + let tests = [( + "616263", + "ddaf35a193617abacc417349ae20413112e6fa4e89a97ea20a9eeee64b55d39a2\ + 192992a274fc1a836ba3c23a3feebbd454d4423643ce80e2a9ac94fa54ca49f", + )]; + + for test in tests.iter() { + hash_test(MessageDigest::sha512(), test); + } + } + + #[test] + fn test_sha512_256() { + let tests = [( + "616263", + "53048e2681941ef99b2e29b76b4c7dabe4c2d0c634fc6d46e0e2f13107e7af23", + )]; + + for test in tests.iter() { + hash_test(MessageDigest::sha512_256(), test); + } + } + + #[test] + fn from_nid() { + assert_eq!( + MessageDigest::from_nid(Nid::SHA256).unwrap().as_ptr(), + MessageDigest::sha256().as_ptr() + ); + } +} diff --git a/crates/boring/src/lib.rs b/crates/boring/src/lib.rs new file mode 100644 index 000000000..ae8dff0be --- /dev/null +++ b/crates/boring/src/lib.rs @@ -0,0 +1,184 @@ +//! Bindings to BoringSSL +//! +//! This crate provides a safe interface to the BoringSSL cryptography library. +//! +//! # Versioning +//! +//! ## Crate versioning +//! +//! The crate and all the related crates (FFI bindings, etc.) are released simultaneously and all +//! bumped to the same version disregard whether particular crate has any API changes or not. +//! However, semantic versioning guarantees still hold, as all the crate versions will be updated +//! based on the crate with most significant changes. +//! +//! ## BoringSSL version +//! +//! By default, the crate aims to statically link with the latest BoringSSL master branch. +//! *Note*: any BoringSSL revision bumps will be released as a major version update of all crates. +//! +//! # Compilation and linking options +//! +//! ## Environment variables +//! +//! This crate uses various environment variables to tweak how boring is built. The variables +//! are all prefixed by `BORING_BSSL_` for non-FIPS builds, and by `BORING_BSSL_FIPS_` for FIPS builds. +//! +//! ## Support for pre-built binaries or custom source +//! +//! While this crate can build BoringSSL on its own, you may want to provide pre-built binaries instead. +//! To do so, specify the environment variable `BORING_BSSL{,_FIPS}_PATH` with the path to the binaries. +//! +//! You can also provide specific headers by setting `BORING_BSSL{,_FIPS}_INCLUDE_PATH`. +//! +//! _Notes_: The crate will look for headers in the`$BORING_BSSL{,_FIPS}_INCLUDE_PATH/openssl/` +//! folder, make sure to place your headers there. +//! +//! In alternative a different path for the BoringSSL source code directory can be specified by setting +//! `BORING_BSSL{,_FIPS}_SOURCE_PATH` which will automatically be compiled during the build process. +//! +//! _Warning_: When providing a different version of BoringSSL make sure to use a compatible one, the +//! crate relies on the presence of certain functions. +//! +//! ## Building with a FIPS-validated module +//! +//! Only BoringCrypto module version `853ca1ea1168dff08011e5d42d94609cc0ca2e27`, as certified with +//! [FIPS 140-2 certificate 4407](https://csrc.nist.gov/projects/cryptographic-module-validation-program/certificate/4407) +//! is supported by this crate. Support is enabled by this crate's `fips` feature. +//! +//! `boring-sys` comes with a test that FIPS is enabled/disabled depending on the feature flag. You can run it as follows: +//! +//! ```bash +//! $ cargo test --features fips fips::is_enabled +//! ``` +//! +//! ## Linking current BoringSSL version with precompiled FIPS-validated module (`bcm.o`) +//! +//! It's possible to link latest supported version of BoringSSL with FIPS-validated crypto module +//! (`bcm.o`). To enable this compilation option one should enable `fips-link-precompiled` +//! compilation feature and provide a `BORING_BSSL_FIPS_PRECOMPILED_BCM_O` env variable with a path to the +//! precompiled FIPS-validated `bcm.o` module. +//! +//! Note that `BORING_BSSL_PRECOMPILED_BCM_O` is never used, as linking BoringSSL with precompiled non-FIPS +//! module is not supported. +//! +//! # Optional patches +//! +//! ## Raw Public Key +//! +//! The crate can be compiled with [RawPublicKey](https://datatracker.ietf.org/doc/html/rfc7250) +//! support by turning on `rpk` compilation feature. +//! +//! ## Experimental post-quantum cryptography +//! +//! The crate can be compiled with [post-quantum cryptography](https://blog.cloudflare.com/post-quantum-for-all/) +//! support by turning on `post-quantum` compilation feature. +//! +//! Upstream BoringSSL support the post-quantum hybrid key agreement `X25519Kyber768Draft00`. Most +//! users should stick to that one. Enabling this feature, adds a few other post-quantum key +//! agreements: +//! +//! - `X25519Kyber768Draft00Old` is the same as `X25519Kyber768Draft00`, but under its old codepoint. +//! -`X25519Kyber512Draft00`. Similar to `X25519Kyber768Draft00`, but uses level 1 parameter set for +//! Kyber. Not recommended. It's useful to test whether the shorter ClientHello upsets fewer middle +//! boxes. +//! - `P256Kyber768Draft00`. Similar again to `X25519Kyber768Draft00`, but uses P256 as classical +//! part. It uses a non-standard codepoint. Not recommended. +//! +//! Presently all these key agreements are deployed by Cloudflare, but we do not guarantee continued +//! support for them. + +#![cfg_attr(docsrs, feature(doc_auto_cfg))] + +#[macro_use] +extern crate bitflags; +#[macro_use] +extern crate foreign_types; +extern crate boringssl_sys as ffi; +extern crate libc; + +#[cfg(test)] +extern crate hex; + +#[doc(inline)] +pub use crate::ffi::init; + +use libc::{c_int, size_t}; + +use crate::error::ErrorStack; + +#[macro_use] +mod macros; + +mod bio; +#[macro_use] +mod util; +pub mod aes; +pub mod asn1; +pub mod base64; +pub mod bn; +pub mod conf; +pub mod derive; +pub mod dh; +pub mod dsa; +pub mod ec; +pub mod ecdsa; +pub mod error; +pub mod ex_data; +pub mod fips; +pub mod hash; +pub mod memcmp; +pub mod nid; +pub mod pkcs12; +pub mod pkcs5; +pub mod pkey; +pub mod rand; +pub mod rsa; +pub mod sha; +pub mod sign; +pub mod srtp; +pub mod ssl; +pub mod stack; +pub mod string; +pub mod symm; +pub mod version; +pub mod x509; + +fn cvt_p(r: *mut T) -> Result<*mut T, ErrorStack> { + if r.is_null() { + Err(ErrorStack::get()) + } else { + Ok(r) + } +} + +fn cvt_0(r: size_t) -> Result { + if r == 0 { + Err(ErrorStack::get()) + } else { + Ok(r) + } +} + +fn cvt_0i(r: c_int) -> Result { + if r == 0 { + Err(ErrorStack::get()) + } else { + Ok(r) + } +} + +fn cvt(r: c_int) -> Result { + if r <= 0 { + Err(ErrorStack::get()) + } else { + Ok(r) + } +} + +fn cvt_n(r: c_int) -> Result { + if r < 0 { + Err(ErrorStack::get()) + } else { + Ok(r) + } +} diff --git a/crates/boring/src/macros.rs b/crates/boring/src/macros.rs new file mode 100644 index 000000000..0dcc5faec --- /dev/null +++ b/crates/boring/src/macros.rs @@ -0,0 +1,261 @@ +macro_rules! private_key_from_pem { + ($(#[$m:meta])* $n:ident, $(#[$m2:meta])* $n2:ident, $(#[$m3:meta])* $n3:ident, $t:ty, $f:path) => { + from_pem!($(#[$m])* $n, $t, $f); + + $(#[$m2])* + pub fn $n2(pem: &[u8], passphrase: &[u8]) -> Result<$t, crate::error::ErrorStack> { + unsafe { + ffi::init(); + let bio = crate::bio::MemBioSlice::new(pem)?; + let passphrase = ::std::ffi::CString::new(passphrase).unwrap(); + cvt_p($f(bio.as_ptr(), + ptr::null_mut(), + None, + passphrase.as_ptr() as *const _ as *mut _)) + .map(|p| ::foreign_types::ForeignType::from_ptr(p)) + } + } + + $(#[$m3])* + pub fn $n3(pem: &[u8], callback: F) -> Result<$t, crate::error::ErrorStack> + where F: FnOnce(&mut [u8]) -> Result + { + unsafe { + ffi::init(); + let mut cb = crate::util::CallbackState::new(callback); + let bio = crate::bio::MemBioSlice::new(pem)?; + cvt_p($f(bio.as_ptr(), + ptr::null_mut(), + Some(crate::util::invoke_passwd_cb::), + &mut cb as *mut _ as *mut _)) + .map(|p| ::foreign_types::ForeignType::from_ptr(p)) + } + } + } +} + +macro_rules! private_key_to_pem { + ($(#[$m:meta])* $n:ident, $(#[$m2:meta])* $n2:ident, $f:path) => { + $(#[$m])* + pub fn $n(&self) -> Result, crate::error::ErrorStack> { + unsafe { + let bio = crate::bio::MemBio::new()?; + cvt($f(bio.as_ptr(), + self.as_ptr(), + ptr::null(), + ptr::null_mut(), + -1, + None, + ptr::null_mut()))?; + Ok(bio.get_buf().to_owned()) + } + } + + $(#[$m2])* + pub fn $n2( + &self, + cipher: crate::symm::Cipher, + passphrase: &[u8] + ) -> Result, crate::error::ErrorStack> { + unsafe { + let bio = crate::bio::MemBio::new()?; + assert!(passphrase.len() <= ::libc::c_int::max_value() as usize); + cvt($f(bio.as_ptr(), + self.as_ptr(), + cipher.as_ptr(), + passphrase.as_ptr() as *const _ as *mut _, + passphrase.len() as ::libc::c_int, + None, + ptr::null_mut()))?; + Ok(bio.get_buf().to_owned()) + } + } + } +} + +macro_rules! to_pem { + ($(#[$m:meta])* $n:ident, $f:path) => { + $(#[$m])* + pub fn $n(&self) -> Result, crate::error::ErrorStack> { + unsafe { + let bio = crate::bio::MemBio::new()?; + cvt($f(bio.as_ptr(), self.as_ptr()))?; + Ok(bio.get_buf().to_owned()) + } + } + } +} + +macro_rules! to_der { + ($(#[$m:meta])* $n:ident, $f:path) => { + $(#[$m])* + pub fn $n(&self) -> Result, crate::error::ErrorStack> { + unsafe { + let len = crate::cvt($f(::foreign_types::ForeignTypeRef::as_ptr(self), + ptr::null_mut()))?; + let mut buf = vec![0; len as usize]; + crate::cvt($f(::foreign_types::ForeignTypeRef::as_ptr(self), + &mut buf.as_mut_ptr()))?; + Ok(buf) + } + } + }; +} + +macro_rules! from_der { + ($(#[$m:meta])* $n:ident, $t:ty, $f:path, $len_ty:ty) => { + $(#[$m])* + pub fn $n(der: &[u8]) -> Result<$t, crate::error::ErrorStack> { + unsafe { + crate::ffi::init(); + let len = ::std::cmp::min(der.len(), <$len_ty>::max_value() as usize) as $len_ty; + crate::cvt_p($f(::std::ptr::null_mut(), &mut der.as_ptr(), len)) + .map(|p| ::foreign_types::ForeignType::from_ptr(p)) + } + } + } +} + +macro_rules! from_pem { + ($(#[$m:meta])* $n:ident, $t:ty, $f:path) => { + $(#[$m])* + pub fn $n(pem: &[u8]) -> Result<$t, crate::error::ErrorStack> { + unsafe { + crate::init(); + let bio = crate::bio::MemBioSlice::new(pem)?; + cvt_p($f(bio.as_ptr(), ::std::ptr::null_mut(), None, ::std::ptr::null_mut())) + .map(|p| ::foreign_types::ForeignType::from_ptr(p)) + } + } + } +} + +macro_rules! foreign_type_and_impl_send_sync { + ( + $(#[$impl_attr:meta])* + type CType = $ctype:ty; + fn drop = $drop:expr; + $(fn clone = $clone:expr;)* + + $(#[$owned_attr:meta])* + pub struct $owned:ident; + ) + => { + foreign_type! { + $(#[$impl_attr])* + $(#[$owned_attr])* + pub unsafe type $owned: Send + Sync { + type CType = $ctype; + fn drop = $drop; + $(fn clone = $clone;)* + } + } + }; +} + +macro_rules! generic_foreign_type_and_impl_send_sync { + ( + $(#[$impl_attr:meta])* + type CType = $ctype:ty; + fn drop = $drop:expr; + $(fn clone = $clone:expr;)* + + $(#[$owned_attr:meta])* + pub struct $owned:ident; + $(#[$borrowed_attr:meta])* + pub struct $borrowed:ident; + ) => { + $(#[$owned_attr])* + pub struct $owned(*mut $ctype, ::std::marker::PhantomData); + + $(#[$impl_attr])* + unsafe impl ::foreign_types::ForeignType for $owned { + type CType = $ctype; + type Ref = $borrowed; + + #[inline] + unsafe fn from_ptr(ptr: *mut $ctype) -> $owned { + $owned(ptr, ::std::marker::PhantomData) + } + + #[inline] + fn as_ptr(&self) -> *mut $ctype { + self.0 + } + } + + impl Drop for $owned { + #[inline] + fn drop(&mut self) { + unsafe { $drop(self.0) } + } + } + + $( + impl Clone for $owned { + #[inline] + fn clone(&self) -> $owned { + unsafe { + let handle: *mut $ctype = $clone(self.0); + ::foreign_types::ForeignType::from_ptr(handle) + } + } + } + + impl ::std::borrow::ToOwned for $borrowed { + type Owned = $owned; + #[inline] + fn to_owned(&self) -> $owned { + unsafe { + let handle: *mut $ctype = + $clone(::foreign_types::ForeignTypeRef::as_ptr(self)); + $crate::ForeignType::from_ptr(handle) + } + } + } + )* + + impl ::std::ops::Deref for $owned { + type Target = $borrowed; + + #[inline] + fn deref(&self) -> &$borrowed { + unsafe { ::foreign_types::ForeignTypeRef::from_ptr(self.0) } + } + } + + impl ::std::ops::DerefMut for $owned { + #[inline] + fn deref_mut(&mut self) -> &mut $borrowed { + unsafe { ::foreign_types::ForeignTypeRef::from_ptr_mut(self.0) } + } + } + + impl ::std::borrow::Borrow<$borrowed> for $owned { + #[inline] + fn borrow(&self) -> &$borrowed { + &**self + } + } + + impl ::std::convert::AsRef<$borrowed> for $owned { + #[inline] + fn as_ref(&self) -> &$borrowed { + &**self + } + } + + $(#[$borrowed_attr])* + pub struct $borrowed(::foreign_types::Opaque, ::std::marker::PhantomData); + + $(#[$impl_attr])* + unsafe impl ::foreign_types::ForeignTypeRef for $borrowed { + type CType = $ctype; + } + + unsafe impl Send for $owned{} + unsafe impl Send for $borrowed{} + unsafe impl Sync for $owned{} + unsafe impl Sync for $borrowed{} + }; +} diff --git a/crates/boring/src/memcmp.rs b/crates/boring/src/memcmp.rs new file mode 100644 index 000000000..c00b55409 --- /dev/null +++ b/crates/boring/src/memcmp.rs @@ -0,0 +1,92 @@ +//! Utilities to safely compare cryptographic values. +//! +//! Extra care must be taken when comparing values in +//! cryptographic code. If done incorrectly, it can lead +//! to a [timing attack](https://en.wikipedia.org/wiki/Timing_attack). +//! By analyzing the time taken to execute parts of a cryptographic +//! algorithm, and attacker can attempt to compromise the +//! cryptosystem. +//! +//! The utilities in this module are designed to be resistant +//! to this type of attack. +//! +//! # Examples +//! +//! To perform a constant-time comparision of two arrays of the same length but different +//! values: +//! +//! ``` +//! use boring::memcmp::eq; +//! +//! // We want to compare `a` to `b` and `c`, without giving +//! // away through timing analysis that `c` is more similar to `a` +//! // than `b`. +//! let a = [0, 0, 0]; +//! let b = [1, 1, 1]; +//! let c = [0, 0, 1]; +//! +//! // These statements will execute in the same amount of time. +//! assert!(!eq(&a, &b)); +//! assert!(!eq(&a, &c)); +//! ``` +use crate::ffi; +use libc::size_t; + +/// Returns `true` iff `a` and `b` contain the same bytes. +/// +/// This operation takes an amount of time dependent on the length of the two +/// arrays given, but is independent of the contents of a and b. +/// +/// # Panics +/// +/// This function will panic the current task if `a` and `b` do not have the same +/// length. +/// +/// # Examples +/// +/// To perform a constant-time comparision of two arrays of the same length but different +/// values: +/// +/// ``` +/// use boring::memcmp::eq; +/// +/// // We want to compare `a` to `b` and `c`, without giving +/// // away through timing analysis that `c` is more similar to `a` +/// // than `b`. +/// let a = [0, 0, 0]; +/// let b = [1, 1, 1]; +/// let c = [0, 0, 1]; +/// +/// // These statements will execute in the same amount of time. +/// assert!(!eq(&a, &b)); +/// assert!(!eq(&a, &c)); +/// ``` +pub fn eq(a: &[u8], b: &[u8]) -> bool { + assert!(a.len() == b.len()); + let ret = unsafe { + ffi::CRYPTO_memcmp( + a.as_ptr() as *const _, + b.as_ptr() as *const _, + a.len() as size_t, + ) + }; + ret == 0 +} + +#[cfg(test)] +mod tests { + use super::eq; + + #[test] + fn test_eq() { + assert!(eq(&[], &[])); + assert!(eq(&[1], &[1])); + assert!(!eq(&[1, 2, 3], &[1, 2, 4])); + } + + #[test] + #[should_panic] + fn test_diff_lens() { + eq(&[], &[1]); + } +} diff --git a/crates/boring/src/nid.rs b/crates/boring/src/nid.rs new file mode 100644 index 000000000..8f9b400a4 --- /dev/null +++ b/crates/boring/src/nid.rs @@ -0,0 +1,1123 @@ +//! A collection of numerical identifiers for OpenSSL objects. +use crate::ffi; +use libc::{c_char, c_int}; + +use std::ffi::CStr; +use std::str; + +use crate::cvt_p; +use crate::error::ErrorStack; + +/// The digest and public-key algorithms associated with a signature. +pub struct SignatureAlgorithms { + /// The signature's digest. + /// + /// If the signature does not specify a digest, this will be `NID::UNDEF`. + pub digest: Nid, + + /// The signature's public-key. + pub pkey: Nid, +} + +/// A numerical identifier for an OpenSSL object. +/// +/// Objects in OpenSSL can have a short name, a long name, and +/// a numerical identifier (NID). For convenience, objects +/// are usually represented in source code using these numeric +/// identifiers. +/// +/// Users should generally not need to create new `Nid`s. +/// +/// # Examples +/// +/// To view the integer representation of a `Nid`: +/// +/// ``` +/// use boring::nid::Nid; +/// +/// assert!(Nid::AES_256_GCM.as_raw() == 901); +/// ``` +/// +/// # External Documentation +/// +/// The following documentation provides context about `Nid`s and their usage +/// in OpenSSL. +/// +/// - [Obj_nid2obj](https://www.openssl.org/docs/man1.1.0/crypto/OBJ_create.html) +#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] +pub struct Nid(c_int); + +#[allow(non_snake_case)] +impl Nid { + /// Create a `Nid` from an integer representation. + pub fn from_raw(raw: c_int) -> Nid { + Nid(raw) + } + + /// Return the integer representation of a `Nid`. + #[allow(clippy::trivially_copy_pass_by_ref)] + pub fn as_raw(&self) -> c_int { + self.0 + } + + /// Returns the `Nid`s of the digest and public key algorithms associated with a signature ID. + /// + /// This corresponds to `OBJ_find_sigid_algs`. + #[allow(clippy::trivially_copy_pass_by_ref)] + pub fn signature_algorithms(&self) -> Option { + unsafe { + let mut digest = 0; + let mut pkey = 0; + if ffi::OBJ_find_sigid_algs(self.0, &mut digest, &mut pkey) == 1 { + Some(SignatureAlgorithms { + digest: Nid(digest), + pkey: Nid(pkey), + }) + } else { + None + } + } + } + + /// Return the string representation of a `Nid` (long) + /// This corresponds to [`OBJ_nid2ln`] + /// + /// [`OBJ_nid2ln`]: https://www.openssl.org/docs/man1.1.0/crypto/OBJ_nid2ln.html + #[allow(clippy::trivially_copy_pass_by_ref)] + pub fn long_name(&self) -> Result<&'static str, ErrorStack> { + unsafe { + cvt_p(ffi::OBJ_nid2ln(self.0) as *mut c_char) + .map(|nameptr| str::from_utf8(CStr::from_ptr(nameptr).to_bytes()).unwrap()) + } + } + + /// Return the string representation of a `Nid` (short) + /// This corresponds to [`OBJ_nid2sn`] + /// + /// [`OBJ_nid2sn`]: https://www.openssl.org/docs/man1.1.0/crypto/OBJ_nid2sn.html + #[allow(clippy::trivially_copy_pass_by_ref)] + pub fn short_name(&self) -> Result<&'static str, ErrorStack> { + unsafe { + cvt_p(ffi::OBJ_nid2sn(self.0) as *mut c_char) + .map(|nameptr| str::from_utf8(CStr::from_ptr(nameptr).to_bytes()).unwrap()) + } + } + + pub const UNDEF: Nid = Nid(ffi::NID_undef); + pub const ITU_T: Nid = Nid(ffi::NID_itu_t); + pub const ISO: Nid = Nid(ffi::NID_iso); + pub const JOINT_ISO_ITU_T: Nid = Nid(ffi::NID_joint_iso_itu_t); + pub const MEMBER_BODY: Nid = Nid(ffi::NID_member_body); + pub const IDENTIFIED_ORGANIZATION: Nid = Nid(ffi::NID_identified_organization); + pub const HMAC_MD5: Nid = Nid(ffi::NID_hmac_md5); + pub const HMAC_SHA1: Nid = Nid(ffi::NID_hmac_sha1); + pub const CERTICOM_ARC: Nid = Nid(ffi::NID_certicom_arc); + pub const INTERNATIONAL_ORGANIZATIONS: Nid = Nid(ffi::NID_international_organizations); + pub const WAP: Nid = Nid(ffi::NID_wap); + pub const WAP_WSG: Nid = Nid(ffi::NID_wap_wsg); + pub const SELECTED_ATTRIBUTE_TYPES: Nid = Nid(ffi::NID_selected_attribute_types); + pub const CLEARANCE: Nid = Nid(ffi::NID_clearance); + pub const ISO_US: Nid = Nid(ffi::NID_ISO_US); + pub const X9_57: Nid = Nid(ffi::NID_X9_57); + pub const X9CM: Nid = Nid(ffi::NID_X9cm); + pub const DSA: Nid = Nid(ffi::NID_dsa); + pub const DSAWITHSHA1: Nid = Nid(ffi::NID_dsaWithSHA1); + pub const ANSI_X9_62: Nid = Nid(ffi::NID_ansi_X9_62); + pub const X9_62_PRIME_FIELD: Nid = Nid(ffi::NID_X9_62_prime_field); + pub const X9_62_CHARACTERISTIC_TWO_FIELD: Nid = Nid(ffi::NID_X9_62_characteristic_two_field); + pub const X9_62_ID_CHARACTERISTIC_TWO_BASIS: Nid = + Nid(ffi::NID_X9_62_id_characteristic_two_basis); + pub const X9_62_ONBASIS: Nid = Nid(ffi::NID_X9_62_onBasis); + pub const X9_62_TPBASIS: Nid = Nid(ffi::NID_X9_62_tpBasis); + pub const X9_62_PPBASIS: Nid = Nid(ffi::NID_X9_62_ppBasis); + pub const X9_62_ID_ECPUBLICKEY: Nid = Nid(ffi::NID_X9_62_id_ecPublicKey); + pub const X9_62_C2PNB163V1: Nid = Nid(ffi::NID_X9_62_c2pnb163v1); + pub const X9_62_C2PNB163V2: Nid = Nid(ffi::NID_X9_62_c2pnb163v2); + pub const X9_62_C2PNB163V3: Nid = Nid(ffi::NID_X9_62_c2pnb163v3); + pub const X9_62_C2PNB176V1: Nid = Nid(ffi::NID_X9_62_c2pnb176v1); + pub const X9_62_C2TNB191V1: Nid = Nid(ffi::NID_X9_62_c2tnb191v1); + pub const X9_62_C2TNB191V2: Nid = Nid(ffi::NID_X9_62_c2tnb191v2); + pub const X9_62_C2TNB191V3: Nid = Nid(ffi::NID_X9_62_c2tnb191v3); + pub const X9_62_C2ONB191V4: Nid = Nid(ffi::NID_X9_62_c2onb191v4); + pub const X9_62_C2ONB191V5: Nid = Nid(ffi::NID_X9_62_c2onb191v5); + pub const X9_62_C2PNB208W1: Nid = Nid(ffi::NID_X9_62_c2pnb208w1); + pub const X9_62_C2TNB239V1: Nid = Nid(ffi::NID_X9_62_c2tnb239v1); + pub const X9_62_C2TNB239V2: Nid = Nid(ffi::NID_X9_62_c2tnb239v2); + pub const X9_62_C2TNB239V3: Nid = Nid(ffi::NID_X9_62_c2tnb239v3); + pub const X9_62_C2ONB239V4: Nid = Nid(ffi::NID_X9_62_c2onb239v4); + pub const X9_62_C2ONB239V5: Nid = Nid(ffi::NID_X9_62_c2onb239v5); + pub const X9_62_C2PNB272W1: Nid = Nid(ffi::NID_X9_62_c2pnb272w1); + pub const X9_62_C2PNB304W1: Nid = Nid(ffi::NID_X9_62_c2pnb304w1); + pub const X9_62_C2TNB359V1: Nid = Nid(ffi::NID_X9_62_c2tnb359v1); + pub const X9_62_C2PNB368W1: Nid = Nid(ffi::NID_X9_62_c2pnb368w1); + pub const X9_62_C2TNB431R1: Nid = Nid(ffi::NID_X9_62_c2tnb431r1); + pub const X9_62_PRIME192V1: Nid = Nid(ffi::NID_X9_62_prime192v1); + pub const X9_62_PRIME192V2: Nid = Nid(ffi::NID_X9_62_prime192v2); + pub const X9_62_PRIME192V3: Nid = Nid(ffi::NID_X9_62_prime192v3); + pub const X9_62_PRIME239V1: Nid = Nid(ffi::NID_X9_62_prime239v1); + pub const X9_62_PRIME239V2: Nid = Nid(ffi::NID_X9_62_prime239v2); + pub const X9_62_PRIME239V3: Nid = Nid(ffi::NID_X9_62_prime239v3); + pub const X9_62_PRIME256V1: Nid = Nid(ffi::NID_X9_62_prime256v1); + pub const ECDSA_WITH_SHA1: Nid = Nid(ffi::NID_ecdsa_with_SHA1); + pub const ECDSA_WITH_RECOMMENDED: Nid = Nid(ffi::NID_ecdsa_with_Recommended); + pub const ECDSA_WITH_SPECIFIED: Nid = Nid(ffi::NID_ecdsa_with_Specified); + pub const ECDSA_WITH_SHA224: Nid = Nid(ffi::NID_ecdsa_with_SHA224); + pub const ECDSA_WITH_SHA256: Nid = Nid(ffi::NID_ecdsa_with_SHA256); + pub const ECDSA_WITH_SHA384: Nid = Nid(ffi::NID_ecdsa_with_SHA384); + pub const ECDSA_WITH_SHA512: Nid = Nid(ffi::NID_ecdsa_with_SHA512); + pub const SECP112R1: Nid = Nid(ffi::NID_secp112r1); + pub const SECP112R2: Nid = Nid(ffi::NID_secp112r2); + pub const SECP128R1: Nid = Nid(ffi::NID_secp128r1); + pub const SECP128R2: Nid = Nid(ffi::NID_secp128r2); + pub const SECP160K1: Nid = Nid(ffi::NID_secp160k1); + pub const SECP160R1: Nid = Nid(ffi::NID_secp160r1); + pub const SECP160R2: Nid = Nid(ffi::NID_secp160r2); + pub const SECP192K1: Nid = Nid(ffi::NID_secp192k1); + pub const SECP224K1: Nid = Nid(ffi::NID_secp224k1); + pub const SECP224R1: Nid = Nid(ffi::NID_secp224r1); + pub const SECP256K1: Nid = Nid(ffi::NID_secp256k1); + pub const SECP384R1: Nid = Nid(ffi::NID_secp384r1); + pub const SECP521R1: Nid = Nid(ffi::NID_secp521r1); + pub const SECT113R1: Nid = Nid(ffi::NID_sect113r1); + pub const SECT113R2: Nid = Nid(ffi::NID_sect113r2); + pub const SECT131R1: Nid = Nid(ffi::NID_sect131r1); + pub const SECT131R2: Nid = Nid(ffi::NID_sect131r2); + pub const SECT163K1: Nid = Nid(ffi::NID_sect163k1); + pub const SECT163R1: Nid = Nid(ffi::NID_sect163r1); + pub const SECT163R2: Nid = Nid(ffi::NID_sect163r2); + pub const SECT193R1: Nid = Nid(ffi::NID_sect193r1); + pub const SECT193R2: Nid = Nid(ffi::NID_sect193r2); + pub const SECT233K1: Nid = Nid(ffi::NID_sect233k1); + pub const SECT233R1: Nid = Nid(ffi::NID_sect233r1); + pub const SECT239K1: Nid = Nid(ffi::NID_sect239k1); + pub const SECT283K1: Nid = Nid(ffi::NID_sect283k1); + pub const SECT283R1: Nid = Nid(ffi::NID_sect283r1); + pub const SECT409K1: Nid = Nid(ffi::NID_sect409k1); + pub const SECT409R1: Nid = Nid(ffi::NID_sect409r1); + pub const SECT571K1: Nid = Nid(ffi::NID_sect571k1); + pub const SECT571R1: Nid = Nid(ffi::NID_sect571r1); + pub const WAP_WSG_IDM_ECID_WTLS1: Nid = Nid(ffi::NID_wap_wsg_idm_ecid_wtls1); + pub const WAP_WSG_IDM_ECID_WTLS3: Nid = Nid(ffi::NID_wap_wsg_idm_ecid_wtls3); + pub const WAP_WSG_IDM_ECID_WTLS4: Nid = Nid(ffi::NID_wap_wsg_idm_ecid_wtls4); + pub const WAP_WSG_IDM_ECID_WTLS5: Nid = Nid(ffi::NID_wap_wsg_idm_ecid_wtls5); + pub const WAP_WSG_IDM_ECID_WTLS6: Nid = Nid(ffi::NID_wap_wsg_idm_ecid_wtls6); + pub const WAP_WSG_IDM_ECID_WTLS7: Nid = Nid(ffi::NID_wap_wsg_idm_ecid_wtls7); + pub const WAP_WSG_IDM_ECID_WTLS8: Nid = Nid(ffi::NID_wap_wsg_idm_ecid_wtls8); + pub const WAP_WSG_IDM_ECID_WTLS9: Nid = Nid(ffi::NID_wap_wsg_idm_ecid_wtls9); + pub const WAP_WSG_IDM_ECID_WTLS10: Nid = Nid(ffi::NID_wap_wsg_idm_ecid_wtls10); + pub const WAP_WSG_IDM_ECID_WTLS11: Nid = Nid(ffi::NID_wap_wsg_idm_ecid_wtls11); + pub const WAP_WSG_IDM_ECID_WTLS12: Nid = Nid(ffi::NID_wap_wsg_idm_ecid_wtls12); + pub const CAST5_CBC: Nid = Nid(ffi::NID_cast5_cbc); + pub const CAST5_ECB: Nid = Nid(ffi::NID_cast5_ecb); + pub const CAST5_CFB64: Nid = Nid(ffi::NID_cast5_cfb64); + pub const CAST5_OFB64: Nid = Nid(ffi::NID_cast5_ofb64); + pub const PBEWITHMD5ANDCAST5_CBC: Nid = Nid(ffi::NID_pbeWithMD5AndCast5_CBC); + pub const ID_PASSWORDBASEDMAC: Nid = Nid(ffi::NID_id_PasswordBasedMAC); + pub const ID_DHBASEDMAC: Nid = Nid(ffi::NID_id_DHBasedMac); + pub const RSADSI: Nid = Nid(ffi::NID_rsadsi); + pub const PKCS: Nid = Nid(ffi::NID_pkcs); + pub const PKCS1: Nid = Nid(ffi::NID_pkcs1); + pub const RSAENCRYPTION: Nid = Nid(ffi::NID_rsaEncryption); + pub const MD2WITHRSAENCRYPTION: Nid = Nid(ffi::NID_md2WithRSAEncryption); + pub const MD4WITHRSAENCRYPTION: Nid = Nid(ffi::NID_md4WithRSAEncryption); + pub const MD5WITHRSAENCRYPTION: Nid = Nid(ffi::NID_md5WithRSAEncryption); + pub const SHA1WITHRSAENCRYPTION: Nid = Nid(ffi::NID_sha1WithRSAEncryption); + pub const RSAESOAEP: Nid = Nid(ffi::NID_rsaesOaep); + pub const MGF1: Nid = Nid(ffi::NID_mgf1); + pub const RSASSAPSS: Nid = Nid(ffi::NID_rsassaPss); + pub const SHA256WITHRSAENCRYPTION: Nid = Nid(ffi::NID_sha256WithRSAEncryption); + pub const SHA384WITHRSAENCRYPTION: Nid = Nid(ffi::NID_sha384WithRSAEncryption); + pub const SHA512WITHRSAENCRYPTION: Nid = Nid(ffi::NID_sha512WithRSAEncryption); + pub const SHA224WITHRSAENCRYPTION: Nid = Nid(ffi::NID_sha224WithRSAEncryption); + pub const PKCS3: Nid = Nid(ffi::NID_pkcs3); + pub const DHKEYAGREEMENT: Nid = Nid(ffi::NID_dhKeyAgreement); + pub const PKCS5: Nid = Nid(ffi::NID_pkcs5); + pub const PBEWITHMD2ANDDES_CBC: Nid = Nid(ffi::NID_pbeWithMD2AndDES_CBC); + pub const PBEWITHMD5ANDDES_CBC: Nid = Nid(ffi::NID_pbeWithMD5AndDES_CBC); + pub const PBEWITHMD2ANDRC2_CBC: Nid = Nid(ffi::NID_pbeWithMD2AndRC2_CBC); + pub const PBEWITHMD5ANDRC2_CBC: Nid = Nid(ffi::NID_pbeWithMD5AndRC2_CBC); + pub const PBEWITHSHA1ANDDES_CBC: Nid = Nid(ffi::NID_pbeWithSHA1AndDES_CBC); + pub const PBEWITHSHA1ANDRC2_CBC: Nid = Nid(ffi::NID_pbeWithSHA1AndRC2_CBC); + pub const ID_PBKDF2: Nid = Nid(ffi::NID_id_pbkdf2); + pub const PBES2: Nid = Nid(ffi::NID_pbes2); + pub const PBMAC1: Nid = Nid(ffi::NID_pbmac1); + pub const PKCS7: Nid = Nid(ffi::NID_pkcs7); + pub const PKCS7_DATA: Nid = Nid(ffi::NID_pkcs7_data); + pub const PKCS7_SIGNED: Nid = Nid(ffi::NID_pkcs7_signed); + pub const PKCS7_ENVELOPED: Nid = Nid(ffi::NID_pkcs7_enveloped); + pub const PKCS7_SIGNEDANDENVELOPED: Nid = Nid(ffi::NID_pkcs7_signedAndEnveloped); + pub const PKCS7_DIGEST: Nid = Nid(ffi::NID_pkcs7_digest); + pub const PKCS7_ENCRYPTED: Nid = Nid(ffi::NID_pkcs7_encrypted); + pub const PKCS9: Nid = Nid(ffi::NID_pkcs9); + pub const PKCS9_EMAILADDRESS: Nid = Nid(ffi::NID_pkcs9_emailAddress); + pub const PKCS9_UNSTRUCTUREDNAME: Nid = Nid(ffi::NID_pkcs9_unstructuredName); + pub const PKCS9_CONTENTTYPE: Nid = Nid(ffi::NID_pkcs9_contentType); + pub const PKCS9_MESSAGEDIGEST: Nid = Nid(ffi::NID_pkcs9_messageDigest); + pub const PKCS9_SIGNINGTIME: Nid = Nid(ffi::NID_pkcs9_signingTime); + pub const PKCS9_COUNTERSIGNATURE: Nid = Nid(ffi::NID_pkcs9_countersignature); + pub const PKCS9_CHALLENGEPASSWORD: Nid = Nid(ffi::NID_pkcs9_challengePassword); + pub const PKCS9_UNSTRUCTUREDADDRESS: Nid = Nid(ffi::NID_pkcs9_unstructuredAddress); + pub const PKCS9_EXTCERTATTRIBUTES: Nid = Nid(ffi::NID_pkcs9_extCertAttributes); + pub const EXT_REQ: Nid = Nid(ffi::NID_ext_req); + pub const SMIMECAPABILITIES: Nid = Nid(ffi::NID_SMIMECapabilities); + pub const SMIME: Nid = Nid(ffi::NID_SMIME); + pub const ID_SMIME_MOD: Nid = Nid(ffi::NID_id_smime_mod); + pub const ID_SMIME_CT: Nid = Nid(ffi::NID_id_smime_ct); + pub const ID_SMIME_AA: Nid = Nid(ffi::NID_id_smime_aa); + pub const ID_SMIME_ALG: Nid = Nid(ffi::NID_id_smime_alg); + pub const ID_SMIME_CD: Nid = Nid(ffi::NID_id_smime_cd); + pub const ID_SMIME_SPQ: Nid = Nid(ffi::NID_id_smime_spq); + pub const ID_SMIME_CTI: Nid = Nid(ffi::NID_id_smime_cti); + pub const ID_SMIME_MOD_CMS: Nid = Nid(ffi::NID_id_smime_mod_cms); + pub const ID_SMIME_MOD_ESS: Nid = Nid(ffi::NID_id_smime_mod_ess); + pub const ID_SMIME_MOD_OID: Nid = Nid(ffi::NID_id_smime_mod_oid); + pub const ID_SMIME_MOD_MSG_V3: Nid = Nid(ffi::NID_id_smime_mod_msg_v3); + pub const ID_SMIME_MOD_ETS_ESIGNATURE_88: Nid = Nid(ffi::NID_id_smime_mod_ets_eSignature_88); + pub const ID_SMIME_MOD_ETS_ESIGNATURE_97: Nid = Nid(ffi::NID_id_smime_mod_ets_eSignature_97); + pub const ID_SMIME_MOD_ETS_ESIGPOLICY_88: Nid = Nid(ffi::NID_id_smime_mod_ets_eSigPolicy_88); + pub const ID_SMIME_MOD_ETS_ESIGPOLICY_97: Nid = Nid(ffi::NID_id_smime_mod_ets_eSigPolicy_97); + pub const ID_SMIME_CT_RECEIPT: Nid = Nid(ffi::NID_id_smime_ct_receipt); + pub const ID_SMIME_CT_AUTHDATA: Nid = Nid(ffi::NID_id_smime_ct_authData); + pub const ID_SMIME_CT_PUBLISHCERT: Nid = Nid(ffi::NID_id_smime_ct_publishCert); + pub const ID_SMIME_CT_TSTINFO: Nid = Nid(ffi::NID_id_smime_ct_TSTInfo); + pub const ID_SMIME_CT_TDTINFO: Nid = Nid(ffi::NID_id_smime_ct_TDTInfo); + pub const ID_SMIME_CT_CONTENTINFO: Nid = Nid(ffi::NID_id_smime_ct_contentInfo); + pub const ID_SMIME_CT_DVCSREQUESTDATA: Nid = Nid(ffi::NID_id_smime_ct_DVCSRequestData); + pub const ID_SMIME_CT_DVCSRESPONSEDATA: Nid = Nid(ffi::NID_id_smime_ct_DVCSResponseData); + pub const ID_SMIME_CT_COMPRESSEDDATA: Nid = Nid(ffi::NID_id_smime_ct_compressedData); + pub const ID_CT_ASCIITEXTWITHCRLF: Nid = Nid(ffi::NID_id_ct_asciiTextWithCRLF); + pub const ID_SMIME_AA_RECEIPTREQUEST: Nid = Nid(ffi::NID_id_smime_aa_receiptRequest); + pub const ID_SMIME_AA_SECURITYLABEL: Nid = Nid(ffi::NID_id_smime_aa_securityLabel); + pub const ID_SMIME_AA_MLEXPANDHISTORY: Nid = Nid(ffi::NID_id_smime_aa_mlExpandHistory); + pub const ID_SMIME_AA_CONTENTHINT: Nid = Nid(ffi::NID_id_smime_aa_contentHint); + pub const ID_SMIME_AA_MSGSIGDIGEST: Nid = Nid(ffi::NID_id_smime_aa_msgSigDigest); + pub const ID_SMIME_AA_ENCAPCONTENTTYPE: Nid = Nid(ffi::NID_id_smime_aa_encapContentType); + pub const ID_SMIME_AA_CONTENTIDENTIFIER: Nid = Nid(ffi::NID_id_smime_aa_contentIdentifier); + pub const ID_SMIME_AA_MACVALUE: Nid = Nid(ffi::NID_id_smime_aa_macValue); + pub const ID_SMIME_AA_EQUIVALENTLABELS: Nid = Nid(ffi::NID_id_smime_aa_equivalentLabels); + pub const ID_SMIME_AA_CONTENTREFERENCE: Nid = Nid(ffi::NID_id_smime_aa_contentReference); + pub const ID_SMIME_AA_ENCRYPKEYPREF: Nid = Nid(ffi::NID_id_smime_aa_encrypKeyPref); + pub const ID_SMIME_AA_SIGNINGCERTIFICATE: Nid = Nid(ffi::NID_id_smime_aa_signingCertificate); + pub const ID_SMIME_AA_SMIMEENCRYPTCERTS: Nid = Nid(ffi::NID_id_smime_aa_smimeEncryptCerts); + pub const ID_SMIME_AA_TIMESTAMPTOKEN: Nid = Nid(ffi::NID_id_smime_aa_timeStampToken); + pub const ID_SMIME_AA_ETS_SIGPOLICYID: Nid = Nid(ffi::NID_id_smime_aa_ets_sigPolicyId); + pub const ID_SMIME_AA_ETS_COMMITMENTTYPE: Nid = Nid(ffi::NID_id_smime_aa_ets_commitmentType); + pub const ID_SMIME_AA_ETS_SIGNERLOCATION: Nid = Nid(ffi::NID_id_smime_aa_ets_signerLocation); + pub const ID_SMIME_AA_ETS_SIGNERATTR: Nid = Nid(ffi::NID_id_smime_aa_ets_signerAttr); + pub const ID_SMIME_AA_ETS_OTHERSIGCERT: Nid = Nid(ffi::NID_id_smime_aa_ets_otherSigCert); + pub const ID_SMIME_AA_ETS_CONTENTTIMESTAMP: Nid = + Nid(ffi::NID_id_smime_aa_ets_contentTimestamp); + pub const ID_SMIME_AA_ETS_CERTIFICATEREFS: Nid = Nid(ffi::NID_id_smime_aa_ets_CertificateRefs); + pub const ID_SMIME_AA_ETS_REVOCATIONREFS: Nid = Nid(ffi::NID_id_smime_aa_ets_RevocationRefs); + pub const ID_SMIME_AA_ETS_CERTVALUES: Nid = Nid(ffi::NID_id_smime_aa_ets_certValues); + pub const ID_SMIME_AA_ETS_REVOCATIONVALUES: Nid = + Nid(ffi::NID_id_smime_aa_ets_revocationValues); + pub const ID_SMIME_AA_ETS_ESCTIMESTAMP: Nid = Nid(ffi::NID_id_smime_aa_ets_escTimeStamp); + pub const ID_SMIME_AA_ETS_CERTCRLTIMESTAMP: Nid = + Nid(ffi::NID_id_smime_aa_ets_certCRLTimestamp); + pub const ID_SMIME_AA_ETS_ARCHIVETIMESTAMP: Nid = + Nid(ffi::NID_id_smime_aa_ets_archiveTimeStamp); + pub const ID_SMIME_AA_SIGNATURETYPE: Nid = Nid(ffi::NID_id_smime_aa_signatureType); + pub const ID_SMIME_AA_DVCS_DVC: Nid = Nid(ffi::NID_id_smime_aa_dvcs_dvc); + pub const ID_SMIME_ALG_ESDHWITH3DES: Nid = Nid(ffi::NID_id_smime_alg_ESDHwith3DES); + pub const ID_SMIME_ALG_ESDHWITHRC2: Nid = Nid(ffi::NID_id_smime_alg_ESDHwithRC2); + pub const ID_SMIME_ALG_3DESWRAP: Nid = Nid(ffi::NID_id_smime_alg_3DESwrap); + pub const ID_SMIME_ALG_RC2WRAP: Nid = Nid(ffi::NID_id_smime_alg_RC2wrap); + pub const ID_SMIME_ALG_ESDH: Nid = Nid(ffi::NID_id_smime_alg_ESDH); + pub const ID_SMIME_ALG_CMS3DESWRAP: Nid = Nid(ffi::NID_id_smime_alg_CMS3DESwrap); + pub const ID_SMIME_ALG_CMSRC2WRAP: Nid = Nid(ffi::NID_id_smime_alg_CMSRC2wrap); + pub const ID_ALG_PWRI_KEK: Nid = Nid(ffi::NID_id_alg_PWRI_KEK); + pub const ID_SMIME_CD_LDAP: Nid = Nid(ffi::NID_id_smime_cd_ldap); + pub const ID_SMIME_SPQ_ETS_SQT_URI: Nid = Nid(ffi::NID_id_smime_spq_ets_sqt_uri); + pub const ID_SMIME_SPQ_ETS_SQT_UNOTICE: Nid = Nid(ffi::NID_id_smime_spq_ets_sqt_unotice); + pub const ID_SMIME_CTI_ETS_PROOFOFORIGIN: Nid = Nid(ffi::NID_id_smime_cti_ets_proofOfOrigin); + pub const ID_SMIME_CTI_ETS_PROOFOFRECEIPT: Nid = Nid(ffi::NID_id_smime_cti_ets_proofOfReceipt); + pub const ID_SMIME_CTI_ETS_PROOFOFDELIVERY: Nid = + Nid(ffi::NID_id_smime_cti_ets_proofOfDelivery); + pub const ID_SMIME_CTI_ETS_PROOFOFSENDER: Nid = Nid(ffi::NID_id_smime_cti_ets_proofOfSender); + pub const ID_SMIME_CTI_ETS_PROOFOFAPPROVAL: Nid = + Nid(ffi::NID_id_smime_cti_ets_proofOfApproval); + pub const ID_SMIME_CTI_ETS_PROOFOFCREATION: Nid = + Nid(ffi::NID_id_smime_cti_ets_proofOfCreation); + pub const FRIENDLYNAME: Nid = Nid(ffi::NID_friendlyName); + pub const LOCALKEYID: Nid = Nid(ffi::NID_localKeyID); + pub const MS_CSP_NAME: Nid = Nid(ffi::NID_ms_csp_name); + pub const LOCALKEYSET: Nid = Nid(ffi::NID_LocalKeySet); + pub const X509CERTIFICATE: Nid = Nid(ffi::NID_x509Certificate); + pub const SDSICERTIFICATE: Nid = Nid(ffi::NID_sdsiCertificate); + pub const X509CRL: Nid = Nid(ffi::NID_x509Crl); + pub const PBE_WITHSHA1AND128BITRC4: Nid = Nid(ffi::NID_pbe_WithSHA1And128BitRC4); + pub const PBE_WITHSHA1AND40BITRC4: Nid = Nid(ffi::NID_pbe_WithSHA1And40BitRC4); + pub const PBE_WITHSHA1AND3_KEY_TRIPLEDES_CBC: Nid = + Nid(ffi::NID_pbe_WithSHA1And3_Key_TripleDES_CBC); + pub const PBE_WITHSHA1AND2_KEY_TRIPLEDES_CBC: Nid = + Nid(ffi::NID_pbe_WithSHA1And2_Key_TripleDES_CBC); + pub const PBE_WITHSHA1AND128BITRC2_CBC: Nid = Nid(ffi::NID_pbe_WithSHA1And128BitRC2_CBC); + pub const PBE_WITHSHA1AND40BITRC2_CBC: Nid = Nid(ffi::NID_pbe_WithSHA1And40BitRC2_CBC); + pub const KEYBAG: Nid = Nid(ffi::NID_keyBag); + pub const PKCS8SHROUDEDKEYBAG: Nid = Nid(ffi::NID_pkcs8ShroudedKeyBag); + pub const CERTBAG: Nid = Nid(ffi::NID_certBag); + pub const CRLBAG: Nid = Nid(ffi::NID_crlBag); + pub const SECRETBAG: Nid = Nid(ffi::NID_secretBag); + pub const SAFECONTENTSBAG: Nid = Nid(ffi::NID_safeContentsBag); + pub const MD2: Nid = Nid(ffi::NID_md2); + pub const MD4: Nid = Nid(ffi::NID_md4); + pub const MD5: Nid = Nid(ffi::NID_md5); + pub const MD5_SHA1: Nid = Nid(ffi::NID_md5_sha1); + pub const HMACWITHMD5: Nid = Nid(ffi::NID_hmacWithMD5); + pub const HMACWITHSHA1: Nid = Nid(ffi::NID_hmacWithSHA1); + pub const HMACWITHSHA224: Nid = Nid(ffi::NID_hmacWithSHA224); + pub const HMACWITHSHA256: Nid = Nid(ffi::NID_hmacWithSHA256); + pub const HMACWITHSHA384: Nid = Nid(ffi::NID_hmacWithSHA384); + pub const HMACWITHSHA512: Nid = Nid(ffi::NID_hmacWithSHA512); + pub const RC2_CBC: Nid = Nid(ffi::NID_rc2_cbc); + pub const RC2_ECB: Nid = Nid(ffi::NID_rc2_ecb); + pub const RC2_CFB64: Nid = Nid(ffi::NID_rc2_cfb64); + pub const RC2_OFB64: Nid = Nid(ffi::NID_rc2_ofb64); + pub const RC2_40_CBC: Nid = Nid(ffi::NID_rc2_40_cbc); + pub const RC2_64_CBC: Nid = Nid(ffi::NID_rc2_64_cbc); + pub const RC4: Nid = Nid(ffi::NID_rc4); + pub const RC4_40: Nid = Nid(ffi::NID_rc4_40); + pub const DES_EDE3_CBC: Nid = Nid(ffi::NID_des_ede3_cbc); + pub const RC5_CBC: Nid = Nid(ffi::NID_rc5_cbc); + pub const RC5_ECB: Nid = Nid(ffi::NID_rc5_ecb); + pub const RC5_CFB64: Nid = Nid(ffi::NID_rc5_cfb64); + pub const RC5_OFB64: Nid = Nid(ffi::NID_rc5_ofb64); + pub const MS_EXT_REQ: Nid = Nid(ffi::NID_ms_ext_req); + pub const MS_CODE_IND: Nid = Nid(ffi::NID_ms_code_ind); + pub const MS_CODE_COM: Nid = Nid(ffi::NID_ms_code_com); + pub const MS_CTL_SIGN: Nid = Nid(ffi::NID_ms_ctl_sign); + pub const MS_SGC: Nid = Nid(ffi::NID_ms_sgc); + pub const MS_EFS: Nid = Nid(ffi::NID_ms_efs); + pub const MS_SMARTCARD_LOGIN: Nid = Nid(ffi::NID_ms_smartcard_login); + pub const MS_UPN: Nid = Nid(ffi::NID_ms_upn); + pub const IDEA_CBC: Nid = Nid(ffi::NID_idea_cbc); + pub const IDEA_ECB: Nid = Nid(ffi::NID_idea_ecb); + pub const IDEA_CFB64: Nid = Nid(ffi::NID_idea_cfb64); + pub const IDEA_OFB64: Nid = Nid(ffi::NID_idea_ofb64); + pub const BF_CBC: Nid = Nid(ffi::NID_bf_cbc); + pub const BF_ECB: Nid = Nid(ffi::NID_bf_ecb); + pub const BF_CFB64: Nid = Nid(ffi::NID_bf_cfb64); + pub const BF_OFB64: Nid = Nid(ffi::NID_bf_ofb64); + pub const ID_PKIX: Nid = Nid(ffi::NID_id_pkix); + pub const ID_PKIX_MOD: Nid = Nid(ffi::NID_id_pkix_mod); + pub const ID_PE: Nid = Nid(ffi::NID_id_pe); + pub const ID_QT: Nid = Nid(ffi::NID_id_qt); + pub const ID_KP: Nid = Nid(ffi::NID_id_kp); + pub const ID_IT: Nid = Nid(ffi::NID_id_it); + pub const ID_PKIP: Nid = Nid(ffi::NID_id_pkip); + pub const ID_ALG: Nid = Nid(ffi::NID_id_alg); + pub const ID_CMC: Nid = Nid(ffi::NID_id_cmc); + pub const ID_ON: Nid = Nid(ffi::NID_id_on); + pub const ID_PDA: Nid = Nid(ffi::NID_id_pda); + pub const ID_ACA: Nid = Nid(ffi::NID_id_aca); + pub const ID_QCS: Nid = Nid(ffi::NID_id_qcs); + pub const ID_CCT: Nid = Nid(ffi::NID_id_cct); + pub const ID_PPL: Nid = Nid(ffi::NID_id_ppl); + pub const ID_AD: Nid = Nid(ffi::NID_id_ad); + pub const ID_PKIX1_EXPLICIT_88: Nid = Nid(ffi::NID_id_pkix1_explicit_88); + pub const ID_PKIX1_IMPLICIT_88: Nid = Nid(ffi::NID_id_pkix1_implicit_88); + pub const ID_PKIX1_EXPLICIT_93: Nid = Nid(ffi::NID_id_pkix1_explicit_93); + pub const ID_PKIX1_IMPLICIT_93: Nid = Nid(ffi::NID_id_pkix1_implicit_93); + pub const ID_MOD_CRMF: Nid = Nid(ffi::NID_id_mod_crmf); + pub const ID_MOD_CMC: Nid = Nid(ffi::NID_id_mod_cmc); + pub const ID_MOD_KEA_PROFILE_88: Nid = Nid(ffi::NID_id_mod_kea_profile_88); + pub const ID_MOD_KEA_PROFILE_93: Nid = Nid(ffi::NID_id_mod_kea_profile_93); + pub const ID_MOD_CMP: Nid = Nid(ffi::NID_id_mod_cmp); + pub const ID_MOD_QUALIFIED_CERT_88: Nid = Nid(ffi::NID_id_mod_qualified_cert_88); + pub const ID_MOD_QUALIFIED_CERT_93: Nid = Nid(ffi::NID_id_mod_qualified_cert_93); + pub const ID_MOD_ATTRIBUTE_CERT: Nid = Nid(ffi::NID_id_mod_attribute_cert); + pub const ID_MOD_TIMESTAMP_PROTOCOL: Nid = Nid(ffi::NID_id_mod_timestamp_protocol); + pub const ID_MOD_OCSP: Nid = Nid(ffi::NID_id_mod_ocsp); + pub const ID_MOD_DVCS: Nid = Nid(ffi::NID_id_mod_dvcs); + pub const ID_MOD_CMP2000: Nid = Nid(ffi::NID_id_mod_cmp2000); + pub const INFO_ACCESS: Nid = Nid(ffi::NID_info_access); + pub const BIOMETRICINFO: Nid = Nid(ffi::NID_biometricInfo); + pub const QCSTATEMENTS: Nid = Nid(ffi::NID_qcStatements); + pub const AC_AUDITENTITY: Nid = Nid(ffi::NID_ac_auditEntity); + pub const AC_TARGETING: Nid = Nid(ffi::NID_ac_targeting); + pub const AACONTROLS: Nid = Nid(ffi::NID_aaControls); + pub const SBGP_IPADDRBLOCK: Nid = Nid(ffi::NID_sbgp_ipAddrBlock); + pub const SBGP_AUTONOMOUSSYSNUM: Nid = Nid(ffi::NID_sbgp_autonomousSysNum); + pub const SBGP_ROUTERIDENTIFIER: Nid = Nid(ffi::NID_sbgp_routerIdentifier); + pub const AC_PROXYING: Nid = Nid(ffi::NID_ac_proxying); + pub const SINFO_ACCESS: Nid = Nid(ffi::NID_sinfo_access); + pub const PROXYCERTINFO: Nid = Nid(ffi::NID_proxyCertInfo); + pub const ID_QT_CPS: Nid = Nid(ffi::NID_id_qt_cps); + pub const ID_QT_UNOTICE: Nid = Nid(ffi::NID_id_qt_unotice); + pub const TEXTNOTICE: Nid = Nid(ffi::NID_textNotice); + pub const SERVER_AUTH: Nid = Nid(ffi::NID_server_auth); + pub const CLIENT_AUTH: Nid = Nid(ffi::NID_client_auth); + pub const CODE_SIGN: Nid = Nid(ffi::NID_code_sign); + pub const EMAIL_PROTECT: Nid = Nid(ffi::NID_email_protect); + pub const IPSECENDSYSTEM: Nid = Nid(ffi::NID_ipsecEndSystem); + pub const IPSECTUNNEL: Nid = Nid(ffi::NID_ipsecTunnel); + pub const IPSECUSER: Nid = Nid(ffi::NID_ipsecUser); + pub const TIME_STAMP: Nid = Nid(ffi::NID_time_stamp); + pub const OCSP_SIGN: Nid = Nid(ffi::NID_OCSP_sign); + pub const DVCS: Nid = Nid(ffi::NID_dvcs); + pub const ID_IT_CAPROTENCCERT: Nid = Nid(ffi::NID_id_it_caProtEncCert); + pub const ID_IT_SIGNKEYPAIRTYPES: Nid = Nid(ffi::NID_id_it_signKeyPairTypes); + pub const ID_IT_ENCKEYPAIRTYPES: Nid = Nid(ffi::NID_id_it_encKeyPairTypes); + pub const ID_IT_PREFERREDSYMMALG: Nid = Nid(ffi::NID_id_it_preferredSymmAlg); + pub const ID_IT_CAKEYUPDATEINFO: Nid = Nid(ffi::NID_id_it_caKeyUpdateInfo); + pub const ID_IT_CURRENTCRL: Nid = Nid(ffi::NID_id_it_currentCRL); + pub const ID_IT_UNSUPPORTEDOIDS: Nid = Nid(ffi::NID_id_it_unsupportedOIDs); + pub const ID_IT_SUBSCRIPTIONREQUEST: Nid = Nid(ffi::NID_id_it_subscriptionRequest); + pub const ID_IT_SUBSCRIPTIONRESPONSE: Nid = Nid(ffi::NID_id_it_subscriptionResponse); + pub const ID_IT_KEYPAIRPARAMREQ: Nid = Nid(ffi::NID_id_it_keyPairParamReq); + pub const ID_IT_KEYPAIRPARAMREP: Nid = Nid(ffi::NID_id_it_keyPairParamRep); + pub const ID_IT_REVPASSPHRASE: Nid = Nid(ffi::NID_id_it_revPassphrase); + pub const ID_IT_IMPLICITCONFIRM: Nid = Nid(ffi::NID_id_it_implicitConfirm); + pub const ID_IT_CONFIRMWAITTIME: Nid = Nid(ffi::NID_id_it_confirmWaitTime); + pub const ID_IT_ORIGPKIMESSAGE: Nid = Nid(ffi::NID_id_it_origPKIMessage); + pub const ID_IT_SUPPLANGTAGS: Nid = Nid(ffi::NID_id_it_suppLangTags); + pub const ID_REGCTRL: Nid = Nid(ffi::NID_id_regCtrl); + pub const ID_REGINFO: Nid = Nid(ffi::NID_id_regInfo); + pub const ID_REGCTRL_REGTOKEN: Nid = Nid(ffi::NID_id_regCtrl_regToken); + pub const ID_REGCTRL_AUTHENTICATOR: Nid = Nid(ffi::NID_id_regCtrl_authenticator); + pub const ID_REGCTRL_PKIPUBLICATIONINFO: Nid = Nid(ffi::NID_id_regCtrl_pkiPublicationInfo); + pub const ID_REGCTRL_PKIARCHIVEOPTIONS: Nid = Nid(ffi::NID_id_regCtrl_pkiArchiveOptions); + pub const ID_REGCTRL_OLDCERTID: Nid = Nid(ffi::NID_id_regCtrl_oldCertID); + pub const ID_REGCTRL_PROTOCOLENCRKEY: Nid = Nid(ffi::NID_id_regCtrl_protocolEncrKey); + pub const ID_REGINFO_UTF8PAIRS: Nid = Nid(ffi::NID_id_regInfo_utf8Pairs); + pub const ID_REGINFO_CERTREQ: Nid = Nid(ffi::NID_id_regInfo_certReq); + pub const ID_ALG_DES40: Nid = Nid(ffi::NID_id_alg_des40); + pub const ID_ALG_NOSIGNATURE: Nid = Nid(ffi::NID_id_alg_noSignature); + pub const ID_ALG_DH_SIG_HMAC_SHA1: Nid = Nid(ffi::NID_id_alg_dh_sig_hmac_sha1); + pub const ID_ALG_DH_POP: Nid = Nid(ffi::NID_id_alg_dh_pop); + pub const ID_CMC_STATUSINFO: Nid = Nid(ffi::NID_id_cmc_statusInfo); + pub const ID_CMC_IDENTIFICATION: Nid = Nid(ffi::NID_id_cmc_identification); + pub const ID_CMC_IDENTITYPROOF: Nid = Nid(ffi::NID_id_cmc_identityProof); + pub const ID_CMC_DATARETURN: Nid = Nid(ffi::NID_id_cmc_dataReturn); + pub const ID_CMC_TRANSACTIONID: Nid = Nid(ffi::NID_id_cmc_transactionId); + pub const ID_CMC_SENDERNONCE: Nid = Nid(ffi::NID_id_cmc_senderNonce); + pub const ID_CMC_RECIPIENTNONCE: Nid = Nid(ffi::NID_id_cmc_recipientNonce); + pub const ID_CMC_ADDEXTENSIONS: Nid = Nid(ffi::NID_id_cmc_addExtensions); + pub const ID_CMC_ENCRYPTEDPOP: Nid = Nid(ffi::NID_id_cmc_encryptedPOP); + pub const ID_CMC_DECRYPTEDPOP: Nid = Nid(ffi::NID_id_cmc_decryptedPOP); + pub const ID_CMC_LRAPOPWITNESS: Nid = Nid(ffi::NID_id_cmc_lraPOPWitness); + pub const ID_CMC_GETCERT: Nid = Nid(ffi::NID_id_cmc_getCert); + pub const ID_CMC_GETCRL: Nid = Nid(ffi::NID_id_cmc_getCRL); + pub const ID_CMC_REVOKEREQUEST: Nid = Nid(ffi::NID_id_cmc_revokeRequest); + pub const ID_CMC_REGINFO: Nid = Nid(ffi::NID_id_cmc_regInfo); + pub const ID_CMC_RESPONSEINFO: Nid = Nid(ffi::NID_id_cmc_responseInfo); + pub const ID_CMC_QUERYPENDING: Nid = Nid(ffi::NID_id_cmc_queryPending); + pub const ID_CMC_POPLINKRANDOM: Nid = Nid(ffi::NID_id_cmc_popLinkRandom); + pub const ID_CMC_POPLINKWITNESS: Nid = Nid(ffi::NID_id_cmc_popLinkWitness); + pub const ID_CMC_CONFIRMCERTACCEPTANCE: Nid = Nid(ffi::NID_id_cmc_confirmCertAcceptance); + pub const ID_ON_PERSONALDATA: Nid = Nid(ffi::NID_id_on_personalData); + pub const ID_ON_PERMANENTIDENTIFIER: Nid = Nid(ffi::NID_id_on_permanentIdentifier); + pub const ID_PDA_DATEOFBIRTH: Nid = Nid(ffi::NID_id_pda_dateOfBirth); + pub const ID_PDA_PLACEOFBIRTH: Nid = Nid(ffi::NID_id_pda_placeOfBirth); + pub const ID_PDA_GENDER: Nid = Nid(ffi::NID_id_pda_gender); + pub const ID_PDA_COUNTRYOFCITIZENSHIP: Nid = Nid(ffi::NID_id_pda_countryOfCitizenship); + pub const ID_PDA_COUNTRYOFRESIDENCE: Nid = Nid(ffi::NID_id_pda_countryOfResidence); + pub const ID_ACA_AUTHENTICATIONINFO: Nid = Nid(ffi::NID_id_aca_authenticationInfo); + pub const ID_ACA_ACCESSIDENTITY: Nid = Nid(ffi::NID_id_aca_accessIdentity); + pub const ID_ACA_CHARGINGIDENTITY: Nid = Nid(ffi::NID_id_aca_chargingIdentity); + pub const ID_ACA_GROUP: Nid = Nid(ffi::NID_id_aca_group); + pub const ID_ACA_ROLE: Nid = Nid(ffi::NID_id_aca_role); + pub const ID_ACA_ENCATTRS: Nid = Nid(ffi::NID_id_aca_encAttrs); + pub const ID_QCS_PKIXQCSYNTAX_V1: Nid = Nid(ffi::NID_id_qcs_pkixQCSyntax_v1); + pub const ID_CCT_CRS: Nid = Nid(ffi::NID_id_cct_crs); + pub const ID_CCT_PKIDATA: Nid = Nid(ffi::NID_id_cct_PKIData); + pub const ID_CCT_PKIRESPONSE: Nid = Nid(ffi::NID_id_cct_PKIResponse); + pub const ID_PPL_ANYLANGUAGE: Nid = Nid(ffi::NID_id_ppl_anyLanguage); + pub const ID_PPL_INHERITALL: Nid = Nid(ffi::NID_id_ppl_inheritAll); + pub const INDEPENDENT: Nid = Nid(ffi::NID_Independent); + pub const AD_OCSP: Nid = Nid(ffi::NID_ad_OCSP); + pub const AD_CA_ISSUERS: Nid = Nid(ffi::NID_ad_ca_issuers); + pub const AD_TIMESTAMPING: Nid = Nid(ffi::NID_ad_timeStamping); + pub const AD_DVCS: Nid = Nid(ffi::NID_ad_dvcs); + pub const CAREPOSITORY: Nid = Nid(ffi::NID_caRepository); + pub const ID_PKIX_OCSP_BASIC: Nid = Nid(ffi::NID_id_pkix_OCSP_basic); + pub const ID_PKIX_OCSP_NONCE: Nid = Nid(ffi::NID_id_pkix_OCSP_Nonce); + pub const ID_PKIX_OCSP_CRLID: Nid = Nid(ffi::NID_id_pkix_OCSP_CrlID); + pub const ID_PKIX_OCSP_ACCEPTABLERESPONSES: Nid = + Nid(ffi::NID_id_pkix_OCSP_acceptableResponses); + pub const ID_PKIX_OCSP_NOCHECK: Nid = Nid(ffi::NID_id_pkix_OCSP_noCheck); + pub const ID_PKIX_OCSP_ARCHIVECUTOFF: Nid = Nid(ffi::NID_id_pkix_OCSP_archiveCutoff); + pub const ID_PKIX_OCSP_SERVICELOCATOR: Nid = Nid(ffi::NID_id_pkix_OCSP_serviceLocator); + pub const ID_PKIX_OCSP_EXTENDEDSTATUS: Nid = Nid(ffi::NID_id_pkix_OCSP_extendedStatus); + pub const ID_PKIX_OCSP_VALID: Nid = Nid(ffi::NID_id_pkix_OCSP_valid); + pub const ID_PKIX_OCSP_PATH: Nid = Nid(ffi::NID_id_pkix_OCSP_path); + pub const ID_PKIX_OCSP_TRUSTROOT: Nid = Nid(ffi::NID_id_pkix_OCSP_trustRoot); + pub const ALGORITHM: Nid = Nid(ffi::NID_algorithm); + pub const MD5WITHRSA: Nid = Nid(ffi::NID_md5WithRSA); + pub const DES_ECB: Nid = Nid(ffi::NID_des_ecb); + pub const DES_CBC: Nid = Nid(ffi::NID_des_cbc); + pub const DES_OFB64: Nid = Nid(ffi::NID_des_ofb64); + pub const DES_CFB64: Nid = Nid(ffi::NID_des_cfb64); + pub const RSASIGNATURE: Nid = Nid(ffi::NID_rsaSignature); + pub const DSA_2: Nid = Nid(ffi::NID_dsa_2); + pub const DSAWITHSHA: Nid = Nid(ffi::NID_dsaWithSHA); + pub const SHAWITHRSAENCRYPTION: Nid = Nid(ffi::NID_shaWithRSAEncryption); + pub const DES_EDE_ECB: Nid = Nid(ffi::NID_des_ede_ecb); + pub const DES_EDE3_ECB: Nid = Nid(ffi::NID_des_ede3_ecb); + pub const DES_EDE_CBC: Nid = Nid(ffi::NID_des_ede_cbc); + pub const DES_EDE_CFB64: Nid = Nid(ffi::NID_des_ede_cfb64); + pub const DES_EDE3_CFB64: Nid = Nid(ffi::NID_des_ede3_cfb64); + pub const DES_EDE_OFB64: Nid = Nid(ffi::NID_des_ede_ofb64); + pub const DES_EDE3_OFB64: Nid = Nid(ffi::NID_des_ede3_ofb64); + pub const DESX_CBC: Nid = Nid(ffi::NID_desx_cbc); + pub const SHA: Nid = Nid(ffi::NID_sha); + pub const SHA1: Nid = Nid(ffi::NID_sha1); + pub const DSAWITHSHA1_2: Nid = Nid(ffi::NID_dsaWithSHA1_2); + pub const SHA1WITHRSA: Nid = Nid(ffi::NID_sha1WithRSA); + pub const RIPEMD160: Nid = Nid(ffi::NID_ripemd160); + pub const RIPEMD160WITHRSA: Nid = Nid(ffi::NID_ripemd160WithRSA); + pub const SXNET: Nid = Nid(ffi::NID_sxnet); + pub const X500: Nid = Nid(ffi::NID_X500); + pub const X509: Nid = Nid(ffi::NID_X509); + pub const COMMONNAME: Nid = Nid(ffi::NID_commonName); + pub const SURNAME: Nid = Nid(ffi::NID_surname); + pub const SERIALNUMBER: Nid = Nid(ffi::NID_serialNumber); + pub const COUNTRYNAME: Nid = Nid(ffi::NID_countryName); + pub const LOCALITYNAME: Nid = Nid(ffi::NID_localityName); + pub const STATEORPROVINCENAME: Nid = Nid(ffi::NID_stateOrProvinceName); + pub const STREETADDRESS: Nid = Nid(ffi::NID_streetAddress); + pub const ORGANIZATIONNAME: Nid = Nid(ffi::NID_organizationName); + pub const ORGANIZATIONALUNITNAME: Nid = Nid(ffi::NID_organizationalUnitName); + pub const TITLE: Nid = Nid(ffi::NID_title); + pub const DESCRIPTION: Nid = Nid(ffi::NID_description); + pub const SEARCHGUIDE: Nid = Nid(ffi::NID_searchGuide); + pub const BUSINESSCATEGORY: Nid = Nid(ffi::NID_businessCategory); + pub const POSTALADDRESS: Nid = Nid(ffi::NID_postalAddress); + pub const POSTALCODE: Nid = Nid(ffi::NID_postalCode); + pub const POSTOFFICEBOX: Nid = Nid(ffi::NID_postOfficeBox); + pub const PHYSICALDELIVERYOFFICENAME: Nid = Nid(ffi::NID_physicalDeliveryOfficeName); + pub const TELEPHONENUMBER: Nid = Nid(ffi::NID_telephoneNumber); + pub const TELEXNUMBER: Nid = Nid(ffi::NID_telexNumber); + pub const TELETEXTERMINALIDENTIFIER: Nid = Nid(ffi::NID_teletexTerminalIdentifier); + pub const FACSIMILETELEPHONENUMBER: Nid = Nid(ffi::NID_facsimileTelephoneNumber); + pub const X121ADDRESS: Nid = Nid(ffi::NID_x121Address); + pub const INTERNATIONALISDNNUMBER: Nid = Nid(ffi::NID_internationaliSDNNumber); + pub const REGISTEREDADDRESS: Nid = Nid(ffi::NID_registeredAddress); + pub const DESTINATIONINDICATOR: Nid = Nid(ffi::NID_destinationIndicator); + pub const PREFERREDDELIVERYMETHOD: Nid = Nid(ffi::NID_preferredDeliveryMethod); + pub const PRESENTATIONADDRESS: Nid = Nid(ffi::NID_presentationAddress); + pub const SUPPORTEDAPPLICATIONCONTEXT: Nid = Nid(ffi::NID_supportedApplicationContext); + pub const MEMBER: Nid = Nid(ffi::NID_member); + pub const OWNER: Nid = Nid(ffi::NID_owner); + pub const ROLEOCCUPANT: Nid = Nid(ffi::NID_roleOccupant); + pub const SEEALSO: Nid = Nid(ffi::NID_seeAlso); + pub const USERPASSWORD: Nid = Nid(ffi::NID_userPassword); + pub const USERCERTIFICATE: Nid = Nid(ffi::NID_userCertificate); + pub const CACERTIFICATE: Nid = Nid(ffi::NID_cACertificate); + pub const AUTHORITYREVOCATIONLIST: Nid = Nid(ffi::NID_authorityRevocationList); + pub const CERTIFICATEREVOCATIONLIST: Nid = Nid(ffi::NID_certificateRevocationList); + pub const CROSSCERTIFICATEPAIR: Nid = Nid(ffi::NID_crossCertificatePair); + pub const NAME: Nid = Nid(ffi::NID_name); + pub const GIVENNAME: Nid = Nid(ffi::NID_givenName); + pub const INITIALS: Nid = Nid(ffi::NID_initials); + pub const GENERATIONQUALIFIER: Nid = Nid(ffi::NID_generationQualifier); + pub const X500UNIQUEIDENTIFIER: Nid = Nid(ffi::NID_x500UniqueIdentifier); + pub const DNQUALIFIER: Nid = Nid(ffi::NID_dnQualifier); + pub const ENHANCEDSEARCHGUIDE: Nid = Nid(ffi::NID_enhancedSearchGuide); + pub const PROTOCOLINFORMATION: Nid = Nid(ffi::NID_protocolInformation); + pub const DISTINGUISHEDNAME: Nid = Nid(ffi::NID_distinguishedName); + pub const UNIQUEMEMBER: Nid = Nid(ffi::NID_uniqueMember); + pub const HOUSEIDENTIFIER: Nid = Nid(ffi::NID_houseIdentifier); + pub const SUPPORTEDALGORITHMS: Nid = Nid(ffi::NID_supportedAlgorithms); + pub const DELTAREVOCATIONLIST: Nid = Nid(ffi::NID_deltaRevocationList); + pub const DMDNAME: Nid = Nid(ffi::NID_dmdName); + pub const PSEUDONYM: Nid = Nid(ffi::NID_pseudonym); + pub const ROLE: Nid = Nid(ffi::NID_role); + pub const X500ALGORITHMS: Nid = Nid(ffi::NID_X500algorithms); + pub const RSA: Nid = Nid(ffi::NID_rsa); + pub const MDC2WITHRSA: Nid = Nid(ffi::NID_mdc2WithRSA); + pub const MDC2: Nid = Nid(ffi::NID_mdc2); + pub const ID_CE: Nid = Nid(ffi::NID_id_ce); + pub const SUBJECT_DIRECTORY_ATTRIBUTES: Nid = Nid(ffi::NID_subject_directory_attributes); + pub const SUBJECT_KEY_IDENTIFIER: Nid = Nid(ffi::NID_subject_key_identifier); + pub const KEY_USAGE: Nid = Nid(ffi::NID_key_usage); + pub const PRIVATE_KEY_USAGE_PERIOD: Nid = Nid(ffi::NID_private_key_usage_period); + pub const SUBJECT_ALT_NAME: Nid = Nid(ffi::NID_subject_alt_name); + pub const ISSUER_ALT_NAME: Nid = Nid(ffi::NID_issuer_alt_name); + pub const BASIC_CONSTRAINTS: Nid = Nid(ffi::NID_basic_constraints); + pub const CRL_NUMBER: Nid = Nid(ffi::NID_crl_number); + pub const CRL_REASON: Nid = Nid(ffi::NID_crl_reason); + pub const INVALIDITY_DATE: Nid = Nid(ffi::NID_invalidity_date); + pub const DELTA_CRL: Nid = Nid(ffi::NID_delta_crl); + pub const ISSUING_DISTRIBUTION_POINT: Nid = Nid(ffi::NID_issuing_distribution_point); + pub const CERTIFICATE_ISSUER: Nid = Nid(ffi::NID_certificate_issuer); + pub const NAME_CONSTRAINTS: Nid = Nid(ffi::NID_name_constraints); + pub const CRL_DISTRIBUTION_POINTS: Nid = Nid(ffi::NID_crl_distribution_points); + pub const CERTIFICATE_POLICIES: Nid = Nid(ffi::NID_certificate_policies); + pub const ANY_POLICY: Nid = Nid(ffi::NID_any_policy); + pub const POLICY_MAPPINGS: Nid = Nid(ffi::NID_policy_mappings); + pub const AUTHORITY_KEY_IDENTIFIER: Nid = Nid(ffi::NID_authority_key_identifier); + pub const POLICY_CONSTRAINTS: Nid = Nid(ffi::NID_policy_constraints); + pub const EXT_KEY_USAGE: Nid = Nid(ffi::NID_ext_key_usage); + pub const FRESHEST_CRL: Nid = Nid(ffi::NID_freshest_crl); + pub const INHIBIT_ANY_POLICY: Nid = Nid(ffi::NID_inhibit_any_policy); + pub const TARGET_INFORMATION: Nid = Nid(ffi::NID_target_information); + pub const NO_REV_AVAIL: Nid = Nid(ffi::NID_no_rev_avail); + pub const ANYEXTENDEDKEYUSAGE: Nid = Nid(ffi::NID_anyExtendedKeyUsage); + pub const NETSCAPE: Nid = Nid(ffi::NID_netscape); + pub const NETSCAPE_CERT_EXTENSION: Nid = Nid(ffi::NID_netscape_cert_extension); + pub const NETSCAPE_DATA_TYPE: Nid = Nid(ffi::NID_netscape_data_type); + pub const NETSCAPE_CERT_TYPE: Nid = Nid(ffi::NID_netscape_cert_type); + pub const NETSCAPE_BASE_URL: Nid = Nid(ffi::NID_netscape_base_url); + pub const NETSCAPE_REVOCATION_URL: Nid = Nid(ffi::NID_netscape_revocation_url); + pub const NETSCAPE_CA_REVOCATION_URL: Nid = Nid(ffi::NID_netscape_ca_revocation_url); + pub const NETSCAPE_RENEWAL_URL: Nid = Nid(ffi::NID_netscape_renewal_url); + pub const NETSCAPE_CA_POLICY_URL: Nid = Nid(ffi::NID_netscape_ca_policy_url); + pub const NETSCAPE_SSL_SERVER_NAME: Nid = Nid(ffi::NID_netscape_ssl_server_name); + pub const NETSCAPE_COMMENT: Nid = Nid(ffi::NID_netscape_comment); + pub const NETSCAPE_CERT_SEQUENCE: Nid = Nid(ffi::NID_netscape_cert_sequence); + pub const NS_SGC: Nid = Nid(ffi::NID_ns_sgc); + pub const ORG: Nid = Nid(ffi::NID_org); + pub const DOD: Nid = Nid(ffi::NID_dod); + pub const IANA: Nid = Nid(ffi::NID_iana); + pub const DIRECTORY: Nid = Nid(ffi::NID_Directory); + pub const MANAGEMENT: Nid = Nid(ffi::NID_Management); + pub const EXPERIMENTAL: Nid = Nid(ffi::NID_Experimental); + pub const PRIVATE: Nid = Nid(ffi::NID_Private); + pub const SECURITY: Nid = Nid(ffi::NID_Security); + pub const SNMPV2: Nid = Nid(ffi::NID_SNMPv2); + pub const MAIL: Nid = Nid(ffi::NID_Mail); + pub const ENTERPRISES: Nid = Nid(ffi::NID_Enterprises); + pub const DCOBJECT: Nid = Nid(ffi::NID_dcObject); + pub const MIME_MHS: Nid = Nid(ffi::NID_mime_mhs); + pub const MIME_MHS_HEADINGS: Nid = Nid(ffi::NID_mime_mhs_headings); + pub const MIME_MHS_BODIES: Nid = Nid(ffi::NID_mime_mhs_bodies); + pub const ID_HEX_PARTIAL_MESSAGE: Nid = Nid(ffi::NID_id_hex_partial_message); + pub const ID_HEX_MULTIPART_MESSAGE: Nid = Nid(ffi::NID_id_hex_multipart_message); + pub const ZLIB_COMPRESSION: Nid = Nid(ffi::NID_zlib_compression); + pub const AES_128_ECB: Nid = Nid(ffi::NID_aes_128_ecb); + pub const AES_128_CBC: Nid = Nid(ffi::NID_aes_128_cbc); + pub const AES_128_OFB128: Nid = Nid(ffi::NID_aes_128_ofb128); + pub const AES_128_CFB128: Nid = Nid(ffi::NID_aes_128_cfb128); + pub const ID_AES128_WRAP: Nid = Nid(ffi::NID_id_aes128_wrap); + pub const AES_128_GCM: Nid = Nid(ffi::NID_aes_128_gcm); + pub const AES_128_CCM: Nid = Nid(ffi::NID_aes_128_ccm); + pub const ID_AES128_WRAP_PAD: Nid = Nid(ffi::NID_id_aes128_wrap_pad); + pub const AES_192_ECB: Nid = Nid(ffi::NID_aes_192_ecb); + pub const AES_192_CBC: Nid = Nid(ffi::NID_aes_192_cbc); + pub const AES_192_OFB128: Nid = Nid(ffi::NID_aes_192_ofb128); + pub const AES_192_CFB128: Nid = Nid(ffi::NID_aes_192_cfb128); + pub const ID_AES192_WRAP: Nid = Nid(ffi::NID_id_aes192_wrap); + pub const AES_192_GCM: Nid = Nid(ffi::NID_aes_192_gcm); + pub const AES_192_CCM: Nid = Nid(ffi::NID_aes_192_ccm); + pub const ID_AES192_WRAP_PAD: Nid = Nid(ffi::NID_id_aes192_wrap_pad); + pub const AES_256_ECB: Nid = Nid(ffi::NID_aes_256_ecb); + pub const AES_256_CBC: Nid = Nid(ffi::NID_aes_256_cbc); + pub const AES_256_OFB128: Nid = Nid(ffi::NID_aes_256_ofb128); + pub const AES_256_CFB128: Nid = Nid(ffi::NID_aes_256_cfb128); + pub const ID_AES256_WRAP: Nid = Nid(ffi::NID_id_aes256_wrap); + pub const AES_256_GCM: Nid = Nid(ffi::NID_aes_256_gcm); + pub const AES_256_CCM: Nid = Nid(ffi::NID_aes_256_ccm); + pub const ID_AES256_WRAP_PAD: Nid = Nid(ffi::NID_id_aes256_wrap_pad); + pub const AES_128_CFB1: Nid = Nid(ffi::NID_aes_128_cfb1); + pub const AES_192_CFB1: Nid = Nid(ffi::NID_aes_192_cfb1); + pub const AES_256_CFB1: Nid = Nid(ffi::NID_aes_256_cfb1); + pub const AES_128_CFB8: Nid = Nid(ffi::NID_aes_128_cfb8); + pub const AES_192_CFB8: Nid = Nid(ffi::NID_aes_192_cfb8); + pub const AES_256_CFB8: Nid = Nid(ffi::NID_aes_256_cfb8); + pub const AES_128_CTR: Nid = Nid(ffi::NID_aes_128_ctr); + pub const AES_192_CTR: Nid = Nid(ffi::NID_aes_192_ctr); + pub const AES_256_CTR: Nid = Nid(ffi::NID_aes_256_ctr); + pub const AES_128_XTS: Nid = Nid(ffi::NID_aes_128_xts); + pub const AES_256_XTS: Nid = Nid(ffi::NID_aes_256_xts); + pub const DES_CFB1: Nid = Nid(ffi::NID_des_cfb1); + pub const DES_CFB8: Nid = Nid(ffi::NID_des_cfb8); + pub const DES_EDE3_CFB1: Nid = Nid(ffi::NID_des_ede3_cfb1); + pub const DES_EDE3_CFB8: Nid = Nid(ffi::NID_des_ede3_cfb8); + pub const SHA256: Nid = Nid(ffi::NID_sha256); + pub const SHA384: Nid = Nid(ffi::NID_sha384); + pub const SHA512: Nid = Nid(ffi::NID_sha512); + pub const SHA512_256: Nid = Nid(ffi::NID_sha512_256); + pub const SHA224: Nid = Nid(ffi::NID_sha224); + pub const DSA_WITH_SHA224: Nid = Nid(ffi::NID_dsa_with_SHA224); + pub const DSA_WITH_SHA256: Nid = Nid(ffi::NID_dsa_with_SHA256); + pub const HOLD_INSTRUCTION_CODE: Nid = Nid(ffi::NID_hold_instruction_code); + pub const HOLD_INSTRUCTION_NONE: Nid = Nid(ffi::NID_hold_instruction_none); + pub const HOLD_INSTRUCTION_CALL_ISSUER: Nid = Nid(ffi::NID_hold_instruction_call_issuer); + pub const HOLD_INSTRUCTION_REJECT: Nid = Nid(ffi::NID_hold_instruction_reject); + pub const DATA: Nid = Nid(ffi::NID_data); + pub const PSS: Nid = Nid(ffi::NID_pss); + pub const UCL: Nid = Nid(ffi::NID_ucl); + pub const PILOT: Nid = Nid(ffi::NID_pilot); + pub const PILOTATTRIBUTETYPE: Nid = Nid(ffi::NID_pilotAttributeType); + pub const PILOTATTRIBUTESYNTAX: Nid = Nid(ffi::NID_pilotAttributeSyntax); + pub const PILOTOBJECTCLASS: Nid = Nid(ffi::NID_pilotObjectClass); + pub const PILOTGROUPS: Nid = Nid(ffi::NID_pilotGroups); + pub const IA5STRINGSYNTAX: Nid = Nid(ffi::NID_iA5StringSyntax); + pub const CASEIGNOREIA5STRINGSYNTAX: Nid = Nid(ffi::NID_caseIgnoreIA5StringSyntax); + pub const PILOTOBJECT: Nid = Nid(ffi::NID_pilotObject); + pub const PILOTPERSON: Nid = Nid(ffi::NID_pilotPerson); + pub const ACCOUNT: Nid = Nid(ffi::NID_account); + pub const DOCUMENT: Nid = Nid(ffi::NID_document); + pub const ROOM: Nid = Nid(ffi::NID_room); + pub const DOCUMENTSERIES: Nid = Nid(ffi::NID_documentSeries); + pub const DOMAIN: Nid = Nid(ffi::NID_Domain); + pub const RFC822LOCALPART: Nid = Nid(ffi::NID_rFC822localPart); + pub const DNSDOMAIN: Nid = Nid(ffi::NID_dNSDomain); + pub const DOMAINRELATEDOBJECT: Nid = Nid(ffi::NID_domainRelatedObject); + pub const FRIENDLYCOUNTRY: Nid = Nid(ffi::NID_friendlyCountry); + pub const SIMPLESECURITYOBJECT: Nid = Nid(ffi::NID_simpleSecurityObject); + pub const PILOTORGANIZATION: Nid = Nid(ffi::NID_pilotOrganization); + pub const PILOTDSA: Nid = Nid(ffi::NID_pilotDSA); + pub const QUALITYLABELLEDDATA: Nid = Nid(ffi::NID_qualityLabelledData); + pub const USERID: Nid = Nid(ffi::NID_userId); + pub const TEXTENCODEDORADDRESS: Nid = Nid(ffi::NID_textEncodedORAddress); + pub const RFC822MAILBOX: Nid = Nid(ffi::NID_rfc822Mailbox); + pub const INFO: Nid = Nid(ffi::NID_info); + pub const FAVOURITEDRINK: Nid = Nid(ffi::NID_favouriteDrink); + pub const ROOMNUMBER: Nid = Nid(ffi::NID_roomNumber); + pub const PHOTO: Nid = Nid(ffi::NID_photo); + pub const USERCLASS: Nid = Nid(ffi::NID_userClass); + pub const HOST: Nid = Nid(ffi::NID_host); + pub const MANAGER: Nid = Nid(ffi::NID_manager); + pub const DOCUMENTIDENTIFIER: Nid = Nid(ffi::NID_documentIdentifier); + pub const DOCUMENTTITLE: Nid = Nid(ffi::NID_documentTitle); + pub const DOCUMENTVERSION: Nid = Nid(ffi::NID_documentVersion); + pub const DOCUMENTAUTHOR: Nid = Nid(ffi::NID_documentAuthor); + pub const DOCUMENTLOCATION: Nid = Nid(ffi::NID_documentLocation); + pub const HOMETELEPHONENUMBER: Nid = Nid(ffi::NID_homeTelephoneNumber); + pub const SECRETARY: Nid = Nid(ffi::NID_secretary); + pub const OTHERMAILBOX: Nid = Nid(ffi::NID_otherMailbox); + pub const LASTMODIFIEDTIME: Nid = Nid(ffi::NID_lastModifiedTime); + pub const LASTMODIFIEDBY: Nid = Nid(ffi::NID_lastModifiedBy); + pub const DOMAINCOMPONENT: Nid = Nid(ffi::NID_domainComponent); + pub const ARECORD: Nid = Nid(ffi::NID_aRecord); + pub const PILOTATTRIBUTETYPE27: Nid = Nid(ffi::NID_pilotAttributeType27); + pub const MXRECORD: Nid = Nid(ffi::NID_mXRecord); + pub const NSRECORD: Nid = Nid(ffi::NID_nSRecord); + pub const SOARECORD: Nid = Nid(ffi::NID_sOARecord); + pub const CNAMERECORD: Nid = Nid(ffi::NID_cNAMERecord); + pub const ASSOCIATEDDOMAIN: Nid = Nid(ffi::NID_associatedDomain); + pub const ASSOCIATEDNAME: Nid = Nid(ffi::NID_associatedName); + pub const HOMEPOSTALADDRESS: Nid = Nid(ffi::NID_homePostalAddress); + pub const PERSONALTITLE: Nid = Nid(ffi::NID_personalTitle); + pub const MOBILETELEPHONENUMBER: Nid = Nid(ffi::NID_mobileTelephoneNumber); + pub const PAGERTELEPHONENUMBER: Nid = Nid(ffi::NID_pagerTelephoneNumber); + pub const FRIENDLYCOUNTRYNAME: Nid = Nid(ffi::NID_friendlyCountryName); + pub const ORGANIZATIONALSTATUS: Nid = Nid(ffi::NID_organizationalStatus); + pub const JANETMAILBOX: Nid = Nid(ffi::NID_janetMailbox); + pub const MAILPREFERENCEOPTION: Nid = Nid(ffi::NID_mailPreferenceOption); + pub const BUILDINGNAME: Nid = Nid(ffi::NID_buildingName); + pub const DSAQUALITY: Nid = Nid(ffi::NID_dSAQuality); + pub const SINGLELEVELQUALITY: Nid = Nid(ffi::NID_singleLevelQuality); + pub const SUBTREEMINIMUMQUALITY: Nid = Nid(ffi::NID_subtreeMinimumQuality); + pub const SUBTREEMAXIMUMQUALITY: Nid = Nid(ffi::NID_subtreeMaximumQuality); + pub const PERSONALSIGNATURE: Nid = Nid(ffi::NID_personalSignature); + pub const DITREDIRECT: Nid = Nid(ffi::NID_dITRedirect); + pub const AUDIO: Nid = Nid(ffi::NID_audio); + pub const DOCUMENTPUBLISHER: Nid = Nid(ffi::NID_documentPublisher); + pub const ID_SET: Nid = Nid(ffi::NID_id_set); + pub const SET_CTYPE: Nid = Nid(ffi::NID_set_ctype); + pub const SET_MSGEXT: Nid = Nid(ffi::NID_set_msgExt); + pub const SET_ATTR: Nid = Nid(ffi::NID_set_attr); + pub const SET_POLICY: Nid = Nid(ffi::NID_set_policy); + pub const SET_CERTEXT: Nid = Nid(ffi::NID_set_certExt); + pub const SET_BRAND: Nid = Nid(ffi::NID_set_brand); + pub const SETCT_PANDATA: Nid = Nid(ffi::NID_setct_PANData); + pub const SETCT_PANTOKEN: Nid = Nid(ffi::NID_setct_PANToken); + pub const SETCT_PANONLY: Nid = Nid(ffi::NID_setct_PANOnly); + pub const SETCT_OIDATA: Nid = Nid(ffi::NID_setct_OIData); + pub const SETCT_PI: Nid = Nid(ffi::NID_setct_PI); + pub const SETCT_PIDATA: Nid = Nid(ffi::NID_setct_PIData); + pub const SETCT_PIDATAUNSIGNED: Nid = Nid(ffi::NID_setct_PIDataUnsigned); + pub const SETCT_HODINPUT: Nid = Nid(ffi::NID_setct_HODInput); + pub const SETCT_AUTHRESBAGGAGE: Nid = Nid(ffi::NID_setct_AuthResBaggage); + pub const SETCT_AUTHREVREQBAGGAGE: Nid = Nid(ffi::NID_setct_AuthRevReqBaggage); + pub const SETCT_AUTHREVRESBAGGAGE: Nid = Nid(ffi::NID_setct_AuthRevResBaggage); + pub const SETCT_CAPTOKENSEQ: Nid = Nid(ffi::NID_setct_CapTokenSeq); + pub const SETCT_PINITRESDATA: Nid = Nid(ffi::NID_setct_PInitResData); + pub const SETCT_PI_TBS: Nid = Nid(ffi::NID_setct_PI_TBS); + pub const SETCT_PRESDATA: Nid = Nid(ffi::NID_setct_PResData); + pub const SETCT_AUTHREQTBS: Nid = Nid(ffi::NID_setct_AuthReqTBS); + pub const SETCT_AUTHRESTBS: Nid = Nid(ffi::NID_setct_AuthResTBS); + pub const SETCT_AUTHRESTBSX: Nid = Nid(ffi::NID_setct_AuthResTBSX); + pub const SETCT_AUTHTOKENTBS: Nid = Nid(ffi::NID_setct_AuthTokenTBS); + pub const SETCT_CAPTOKENDATA: Nid = Nid(ffi::NID_setct_CapTokenData); + pub const SETCT_CAPTOKENTBS: Nid = Nid(ffi::NID_setct_CapTokenTBS); + pub const SETCT_ACQCARDCODEMSG: Nid = Nid(ffi::NID_setct_AcqCardCodeMsg); + pub const SETCT_AUTHREVREQTBS: Nid = Nid(ffi::NID_setct_AuthRevReqTBS); + pub const SETCT_AUTHREVRESDATA: Nid = Nid(ffi::NID_setct_AuthRevResData); + pub const SETCT_AUTHREVRESTBS: Nid = Nid(ffi::NID_setct_AuthRevResTBS); + pub const SETCT_CAPREQTBS: Nid = Nid(ffi::NID_setct_CapReqTBS); + pub const SETCT_CAPREQTBSX: Nid = Nid(ffi::NID_setct_CapReqTBSX); + pub const SETCT_CAPRESDATA: Nid = Nid(ffi::NID_setct_CapResData); + pub const SETCT_CAPREVREQTBS: Nid = Nid(ffi::NID_setct_CapRevReqTBS); + pub const SETCT_CAPREVREQTBSX: Nid = Nid(ffi::NID_setct_CapRevReqTBSX); + pub const SETCT_CAPREVRESDATA: Nid = Nid(ffi::NID_setct_CapRevResData); + pub const SETCT_CREDREQTBS: Nid = Nid(ffi::NID_setct_CredReqTBS); + pub const SETCT_CREDREQTBSX: Nid = Nid(ffi::NID_setct_CredReqTBSX); + pub const SETCT_CREDRESDATA: Nid = Nid(ffi::NID_setct_CredResData); + pub const SETCT_CREDREVREQTBS: Nid = Nid(ffi::NID_setct_CredRevReqTBS); + pub const SETCT_CREDREVREQTBSX: Nid = Nid(ffi::NID_setct_CredRevReqTBSX); + pub const SETCT_CREDREVRESDATA: Nid = Nid(ffi::NID_setct_CredRevResData); + pub const SETCT_PCERTREQDATA: Nid = Nid(ffi::NID_setct_PCertReqData); + pub const SETCT_PCERTRESTBS: Nid = Nid(ffi::NID_setct_PCertResTBS); + pub const SETCT_BATCHADMINREQDATA: Nid = Nid(ffi::NID_setct_BatchAdminReqData); + pub const SETCT_BATCHADMINRESDATA: Nid = Nid(ffi::NID_setct_BatchAdminResData); + pub const SETCT_CARDCINITRESTBS: Nid = Nid(ffi::NID_setct_CardCInitResTBS); + pub const SETCT_MEAQCINITRESTBS: Nid = Nid(ffi::NID_setct_MeAqCInitResTBS); + pub const SETCT_REGFORMRESTBS: Nid = Nid(ffi::NID_setct_RegFormResTBS); + pub const SETCT_CERTREQDATA: Nid = Nid(ffi::NID_setct_CertReqData); + pub const SETCT_CERTREQTBS: Nid = Nid(ffi::NID_setct_CertReqTBS); + pub const SETCT_CERTRESDATA: Nid = Nid(ffi::NID_setct_CertResData); + pub const SETCT_CERTINQREQTBS: Nid = Nid(ffi::NID_setct_CertInqReqTBS); + pub const SETCT_ERRORTBS: Nid = Nid(ffi::NID_setct_ErrorTBS); + pub const SETCT_PIDUALSIGNEDTBE: Nid = Nid(ffi::NID_setct_PIDualSignedTBE); + pub const SETCT_PIUNSIGNEDTBE: Nid = Nid(ffi::NID_setct_PIUnsignedTBE); + pub const SETCT_AUTHREQTBE: Nid = Nid(ffi::NID_setct_AuthReqTBE); + pub const SETCT_AUTHRESTBE: Nid = Nid(ffi::NID_setct_AuthResTBE); + pub const SETCT_AUTHRESTBEX: Nid = Nid(ffi::NID_setct_AuthResTBEX); + pub const SETCT_AUTHTOKENTBE: Nid = Nid(ffi::NID_setct_AuthTokenTBE); + pub const SETCT_CAPTOKENTBE: Nid = Nid(ffi::NID_setct_CapTokenTBE); + pub const SETCT_CAPTOKENTBEX: Nid = Nid(ffi::NID_setct_CapTokenTBEX); + pub const SETCT_ACQCARDCODEMSGTBE: Nid = Nid(ffi::NID_setct_AcqCardCodeMsgTBE); + pub const SETCT_AUTHREVREQTBE: Nid = Nid(ffi::NID_setct_AuthRevReqTBE); + pub const SETCT_AUTHREVRESTBE: Nid = Nid(ffi::NID_setct_AuthRevResTBE); + pub const SETCT_AUTHREVRESTBEB: Nid = Nid(ffi::NID_setct_AuthRevResTBEB); + pub const SETCT_CAPREQTBE: Nid = Nid(ffi::NID_setct_CapReqTBE); + pub const SETCT_CAPREQTBEX: Nid = Nid(ffi::NID_setct_CapReqTBEX); + pub const SETCT_CAPRESTBE: Nid = Nid(ffi::NID_setct_CapResTBE); + pub const SETCT_CAPREVREQTBE: Nid = Nid(ffi::NID_setct_CapRevReqTBE); + pub const SETCT_CAPREVREQTBEX: Nid = Nid(ffi::NID_setct_CapRevReqTBEX); + pub const SETCT_CAPREVRESTBE: Nid = Nid(ffi::NID_setct_CapRevResTBE); + pub const SETCT_CREDREQTBE: Nid = Nid(ffi::NID_setct_CredReqTBE); + pub const SETCT_CREDREQTBEX: Nid = Nid(ffi::NID_setct_CredReqTBEX); + pub const SETCT_CREDRESTBE: Nid = Nid(ffi::NID_setct_CredResTBE); + pub const SETCT_CREDREVREQTBE: Nid = Nid(ffi::NID_setct_CredRevReqTBE); + pub const SETCT_CREDREVREQTBEX: Nid = Nid(ffi::NID_setct_CredRevReqTBEX); + pub const SETCT_CREDREVRESTBE: Nid = Nid(ffi::NID_setct_CredRevResTBE); + pub const SETCT_BATCHADMINREQTBE: Nid = Nid(ffi::NID_setct_BatchAdminReqTBE); + pub const SETCT_BATCHADMINRESTBE: Nid = Nid(ffi::NID_setct_BatchAdminResTBE); + pub const SETCT_REGFORMREQTBE: Nid = Nid(ffi::NID_setct_RegFormReqTBE); + pub const SETCT_CERTREQTBE: Nid = Nid(ffi::NID_setct_CertReqTBE); + pub const SETCT_CERTREQTBEX: Nid = Nid(ffi::NID_setct_CertReqTBEX); + pub const SETCT_CERTRESTBE: Nid = Nid(ffi::NID_setct_CertResTBE); + pub const SETCT_CRLNOTIFICATIONTBS: Nid = Nid(ffi::NID_setct_CRLNotificationTBS); + pub const SETCT_CRLNOTIFICATIONRESTBS: Nid = Nid(ffi::NID_setct_CRLNotificationResTBS); + pub const SETCT_BCIDISTRIBUTIONTBS: Nid = Nid(ffi::NID_setct_BCIDistributionTBS); + pub const SETEXT_GENCRYPT: Nid = Nid(ffi::NID_setext_genCrypt); + pub const SETEXT_MIAUTH: Nid = Nid(ffi::NID_setext_miAuth); + pub const SETEXT_PINSECURE: Nid = Nid(ffi::NID_setext_pinSecure); + pub const SETEXT_PINANY: Nid = Nid(ffi::NID_setext_pinAny); + pub const SETEXT_TRACK2: Nid = Nid(ffi::NID_setext_track2); + pub const SETEXT_CV: Nid = Nid(ffi::NID_setext_cv); + pub const SET_POLICY_ROOT: Nid = Nid(ffi::NID_set_policy_root); + pub const SETCEXT_HASHEDROOT: Nid = Nid(ffi::NID_setCext_hashedRoot); + pub const SETCEXT_CERTTYPE: Nid = Nid(ffi::NID_setCext_certType); + pub const SETCEXT_MERCHDATA: Nid = Nid(ffi::NID_setCext_merchData); + pub const SETCEXT_CCERTREQUIRED: Nid = Nid(ffi::NID_setCext_cCertRequired); + pub const SETCEXT_TUNNELING: Nid = Nid(ffi::NID_setCext_tunneling); + pub const SETCEXT_SETEXT: Nid = Nid(ffi::NID_setCext_setExt); + pub const SETCEXT_SETQUALF: Nid = Nid(ffi::NID_setCext_setQualf); + pub const SETCEXT_PGWYCAPABILITIES: Nid = Nid(ffi::NID_setCext_PGWYcapabilities); + pub const SETCEXT_TOKENIDENTIFIER: Nid = Nid(ffi::NID_setCext_TokenIdentifier); + pub const SETCEXT_TRACK2DATA: Nid = Nid(ffi::NID_setCext_Track2Data); + pub const SETCEXT_TOKENTYPE: Nid = Nid(ffi::NID_setCext_TokenType); + pub const SETCEXT_ISSUERCAPABILITIES: Nid = Nid(ffi::NID_setCext_IssuerCapabilities); + pub const SETATTR_CERT: Nid = Nid(ffi::NID_setAttr_Cert); + pub const SETATTR_PGWYCAP: Nid = Nid(ffi::NID_setAttr_PGWYcap); + pub const SETATTR_TOKENTYPE: Nid = Nid(ffi::NID_setAttr_TokenType); + pub const SETATTR_ISSCAP: Nid = Nid(ffi::NID_setAttr_IssCap); + pub const SET_ROOTKEYTHUMB: Nid = Nid(ffi::NID_set_rootKeyThumb); + pub const SET_ADDPOLICY: Nid = Nid(ffi::NID_set_addPolicy); + pub const SETATTR_TOKEN_EMV: Nid = Nid(ffi::NID_setAttr_Token_EMV); + pub const SETATTR_TOKEN_B0PRIME: Nid = Nid(ffi::NID_setAttr_Token_B0Prime); + pub const SETATTR_ISSCAP_CVM: Nid = Nid(ffi::NID_setAttr_IssCap_CVM); + pub const SETATTR_ISSCAP_T2: Nid = Nid(ffi::NID_setAttr_IssCap_T2); + pub const SETATTR_ISSCAP_SIG: Nid = Nid(ffi::NID_setAttr_IssCap_Sig); + pub const SETATTR_GENCRYPTGRM: Nid = Nid(ffi::NID_setAttr_GenCryptgrm); + pub const SETATTR_T2ENC: Nid = Nid(ffi::NID_setAttr_T2Enc); + pub const SETATTR_T2CLEARTXT: Nid = Nid(ffi::NID_setAttr_T2cleartxt); + pub const SETATTR_TOKICCSIG: Nid = Nid(ffi::NID_setAttr_TokICCsig); + pub const SETATTR_SECDEVSIG: Nid = Nid(ffi::NID_setAttr_SecDevSig); + pub const SET_BRAND_IATA_ATA: Nid = Nid(ffi::NID_set_brand_IATA_ATA); + pub const SET_BRAND_DINERS: Nid = Nid(ffi::NID_set_brand_Diners); + pub const SET_BRAND_AMERICANEXPRESS: Nid = Nid(ffi::NID_set_brand_AmericanExpress); + pub const SET_BRAND_JCB: Nid = Nid(ffi::NID_set_brand_JCB); + pub const SET_BRAND_VISA: Nid = Nid(ffi::NID_set_brand_Visa); + pub const SET_BRAND_MASTERCARD: Nid = Nid(ffi::NID_set_brand_MasterCard); + pub const SET_BRAND_NOVUS: Nid = Nid(ffi::NID_set_brand_Novus); + pub const DES_CDMF: Nid = Nid(ffi::NID_des_cdmf); + pub const RSAOAEPENCRYPTIONSET: Nid = Nid(ffi::NID_rsaOAEPEncryptionSET); + pub const IPSEC3: Nid = Nid(ffi::NID_ipsec3); + pub const IPSEC4: Nid = Nid(ffi::NID_ipsec4); + pub const WHIRLPOOL: Nid = Nid(ffi::NID_whirlpool); + pub const CRYPTOPRO: Nid = Nid(ffi::NID_cryptopro); + pub const CRYPTOCOM: Nid = Nid(ffi::NID_cryptocom); + pub const ID_GOSTR3411_94_WITH_GOSTR3410_2001: Nid = + Nid(ffi::NID_id_GostR3411_94_with_GostR3410_2001); + pub const ID_GOSTR3411_94_WITH_GOSTR3410_94: Nid = + Nid(ffi::NID_id_GostR3411_94_with_GostR3410_94); + pub const ID_GOSTR3411_94: Nid = Nid(ffi::NID_id_GostR3411_94); + pub const ID_HMACGOSTR3411_94: Nid = Nid(ffi::NID_id_HMACGostR3411_94); + pub const ID_GOSTR3410_2001: Nid = Nid(ffi::NID_id_GostR3410_2001); + pub const ID_GOSTR3410_94: Nid = Nid(ffi::NID_id_GostR3410_94); + pub const ID_GOST28147_89: Nid = Nid(ffi::NID_id_Gost28147_89); + pub const GOST89_CNT: Nid = Nid(ffi::NID_gost89_cnt); + pub const ID_GOST28147_89_MAC: Nid = Nid(ffi::NID_id_Gost28147_89_MAC); + pub const ID_GOSTR3411_94_PRF: Nid = Nid(ffi::NID_id_GostR3411_94_prf); + pub const ID_GOSTR3410_2001DH: Nid = Nid(ffi::NID_id_GostR3410_2001DH); + pub const ID_GOSTR3410_94DH: Nid = Nid(ffi::NID_id_GostR3410_94DH); + pub const ID_GOST28147_89_CRYPTOPRO_KEYMESHING: Nid = + Nid(ffi::NID_id_Gost28147_89_CryptoPro_KeyMeshing); + pub const ID_GOST28147_89_NONE_KEYMESHING: Nid = Nid(ffi::NID_id_Gost28147_89_None_KeyMeshing); + pub const ID_GOSTR3411_94_TESTPARAMSET: Nid = Nid(ffi::NID_id_GostR3411_94_TestParamSet); + pub const ID_GOSTR3411_94_CRYPTOPROPARAMSET: Nid = + Nid(ffi::NID_id_GostR3411_94_CryptoProParamSet); + pub const ID_GOST28147_89_TESTPARAMSET: Nid = Nid(ffi::NID_id_Gost28147_89_TestParamSet); + pub const ID_GOST28147_89_CRYPTOPRO_A_PARAMSET: Nid = + Nid(ffi::NID_id_Gost28147_89_CryptoPro_A_ParamSet); + pub const ID_GOST28147_89_CRYPTOPRO_B_PARAMSET: Nid = + Nid(ffi::NID_id_Gost28147_89_CryptoPro_B_ParamSet); + pub const ID_GOST28147_89_CRYPTOPRO_C_PARAMSET: Nid = + Nid(ffi::NID_id_Gost28147_89_CryptoPro_C_ParamSet); + pub const ID_GOST28147_89_CRYPTOPRO_D_PARAMSET: Nid = + Nid(ffi::NID_id_Gost28147_89_CryptoPro_D_ParamSet); + pub const ID_GOST28147_89_CRYPTOPRO_OSCAR_1_1_PARAMSET: Nid = + Nid(ffi::NID_id_Gost28147_89_CryptoPro_Oscar_1_1_ParamSet); + pub const ID_GOST28147_89_CRYPTOPRO_OSCAR_1_0_PARAMSET: Nid = + Nid(ffi::NID_id_Gost28147_89_CryptoPro_Oscar_1_0_ParamSet); + pub const ID_GOST28147_89_CRYPTOPRO_RIC_1_PARAMSET: Nid = + Nid(ffi::NID_id_Gost28147_89_CryptoPro_RIC_1_ParamSet); + pub const ID_GOSTR3410_94_TESTPARAMSET: Nid = Nid(ffi::NID_id_GostR3410_94_TestParamSet); + pub const ID_GOSTR3410_94_CRYPTOPRO_A_PARAMSET: Nid = + Nid(ffi::NID_id_GostR3410_94_CryptoPro_A_ParamSet); + pub const ID_GOSTR3410_94_CRYPTOPRO_B_PARAMSET: Nid = + Nid(ffi::NID_id_GostR3410_94_CryptoPro_B_ParamSet); + pub const ID_GOSTR3410_94_CRYPTOPRO_C_PARAMSET: Nid = + Nid(ffi::NID_id_GostR3410_94_CryptoPro_C_ParamSet); + pub const ID_GOSTR3410_94_CRYPTOPRO_D_PARAMSET: Nid = + Nid(ffi::NID_id_GostR3410_94_CryptoPro_D_ParamSet); + pub const ID_GOSTR3410_94_CRYPTOPRO_XCHA_PARAMSET: Nid = + Nid(ffi::NID_id_GostR3410_94_CryptoPro_XchA_ParamSet); + pub const ID_GOSTR3410_94_CRYPTOPRO_XCHB_PARAMSET: Nid = + Nid(ffi::NID_id_GostR3410_94_CryptoPro_XchB_ParamSet); + pub const ID_GOSTR3410_94_CRYPTOPRO_XCHC_PARAMSET: Nid = + Nid(ffi::NID_id_GostR3410_94_CryptoPro_XchC_ParamSet); + pub const ID_GOSTR3410_2001_TESTPARAMSET: Nid = Nid(ffi::NID_id_GostR3410_2001_TestParamSet); + pub const ID_GOSTR3410_2001_CRYPTOPRO_A_PARAMSET: Nid = + Nid(ffi::NID_id_GostR3410_2001_CryptoPro_A_ParamSet); + pub const ID_GOSTR3410_2001_CRYPTOPRO_B_PARAMSET: Nid = + Nid(ffi::NID_id_GostR3410_2001_CryptoPro_B_ParamSet); + pub const ID_GOSTR3410_2001_CRYPTOPRO_C_PARAMSET: Nid = + Nid(ffi::NID_id_GostR3410_2001_CryptoPro_C_ParamSet); + pub const ID_GOSTR3410_2001_CRYPTOPRO_XCHA_PARAMSET: Nid = + Nid(ffi::NID_id_GostR3410_2001_CryptoPro_XchA_ParamSet); + pub const ID_GOSTR3410_2001_CRYPTOPRO_XCHB_PARAMSET: Nid = + Nid(ffi::NID_id_GostR3410_2001_CryptoPro_XchB_ParamSet); + pub const ID_GOSTR3410_94_A: Nid = Nid(ffi::NID_id_GostR3410_94_a); + pub const ID_GOSTR3410_94_ABIS: Nid = Nid(ffi::NID_id_GostR3410_94_aBis); + pub const ID_GOSTR3410_94_B: Nid = Nid(ffi::NID_id_GostR3410_94_b); + pub const ID_GOSTR3410_94_BBIS: Nid = Nid(ffi::NID_id_GostR3410_94_bBis); + pub const ID_GOST28147_89_CC: Nid = Nid(ffi::NID_id_Gost28147_89_cc); + pub const ID_GOSTR3410_94_CC: Nid = Nid(ffi::NID_id_GostR3410_94_cc); + pub const ID_GOSTR3410_2001_CC: Nid = Nid(ffi::NID_id_GostR3410_2001_cc); + pub const ID_GOSTR3411_94_WITH_GOSTR3410_94_CC: Nid = + Nid(ffi::NID_id_GostR3411_94_with_GostR3410_94_cc); + pub const ID_GOSTR3411_94_WITH_GOSTR3410_2001_CC: Nid = + Nid(ffi::NID_id_GostR3411_94_with_GostR3410_2001_cc); + pub const ID_GOSTR3410_2001_PARAMSET_CC: Nid = Nid(ffi::NID_id_GostR3410_2001_ParamSet_cc); + pub const CAMELLIA_128_CBC: Nid = Nid(ffi::NID_camellia_128_cbc); + pub const CAMELLIA_192_CBC: Nid = Nid(ffi::NID_camellia_192_cbc); + pub const CAMELLIA_256_CBC: Nid = Nid(ffi::NID_camellia_256_cbc); + pub const ID_CAMELLIA128_WRAP: Nid = Nid(ffi::NID_id_camellia128_wrap); + pub const ID_CAMELLIA192_WRAP: Nid = Nid(ffi::NID_id_camellia192_wrap); + pub const ID_CAMELLIA256_WRAP: Nid = Nid(ffi::NID_id_camellia256_wrap); + pub const CAMELLIA_128_ECB: Nid = Nid(ffi::NID_camellia_128_ecb); + pub const CAMELLIA_128_OFB128: Nid = Nid(ffi::NID_camellia_128_ofb128); + pub const CAMELLIA_128_CFB128: Nid = Nid(ffi::NID_camellia_128_cfb128); + pub const CAMELLIA_192_ECB: Nid = Nid(ffi::NID_camellia_192_ecb); + pub const CAMELLIA_192_OFB128: Nid = Nid(ffi::NID_camellia_192_ofb128); + pub const CAMELLIA_192_CFB128: Nid = Nid(ffi::NID_camellia_192_cfb128); + pub const CAMELLIA_256_ECB: Nid = Nid(ffi::NID_camellia_256_ecb); + pub const CAMELLIA_256_OFB128: Nid = Nid(ffi::NID_camellia_256_ofb128); + pub const CAMELLIA_256_CFB128: Nid = Nid(ffi::NID_camellia_256_cfb128); + pub const CAMELLIA_128_CFB1: Nid = Nid(ffi::NID_camellia_128_cfb1); + pub const CAMELLIA_192_CFB1: Nid = Nid(ffi::NID_camellia_192_cfb1); + pub const CAMELLIA_256_CFB1: Nid = Nid(ffi::NID_camellia_256_cfb1); + pub const CAMELLIA_128_CFB8: Nid = Nid(ffi::NID_camellia_128_cfb8); + pub const CAMELLIA_192_CFB8: Nid = Nid(ffi::NID_camellia_192_cfb8); + pub const CAMELLIA_256_CFB8: Nid = Nid(ffi::NID_camellia_256_cfb8); + pub const KISA: Nid = Nid(ffi::NID_kisa); + pub const SEED_ECB: Nid = Nid(ffi::NID_seed_ecb); + pub const SEED_CBC: Nid = Nid(ffi::NID_seed_cbc); + pub const SEED_CFB128: Nid = Nid(ffi::NID_seed_cfb128); + pub const SEED_OFB128: Nid = Nid(ffi::NID_seed_ofb128); + pub const HMAC: Nid = Nid(ffi::NID_hmac); + pub const CMAC: Nid = Nid(ffi::NID_cmac); + pub const RC4_HMAC_MD5: Nid = Nid(ffi::NID_rc4_hmac_md5); + pub const AES_128_CBC_HMAC_SHA1: Nid = Nid(ffi::NID_aes_128_cbc_hmac_sha1); + pub const AES_192_CBC_HMAC_SHA1: Nid = Nid(ffi::NID_aes_192_cbc_hmac_sha1); + pub const AES_256_CBC_HMAC_SHA1: Nid = Nid(ffi::NID_aes_256_cbc_hmac_sha1); +} + +#[cfg(test)] +mod test { + use super::Nid; + + #[test] + fn signature_digest() { + let algs = Nid::SHA256WITHRSAENCRYPTION.signature_algorithms().unwrap(); + assert_eq!(algs.digest, Nid::SHA256); + assert_eq!(algs.pkey, Nid::RSAENCRYPTION); + } + + #[test] + fn test_long_name_conversion() { + let common_name = Nid::COMMONNAME; + let organizational_unit_name = Nid::ORGANIZATIONALUNITNAME; + let aes256_cbc_hmac_sha1 = Nid::AES_256_CBC_HMAC_SHA1; + let id_cmc_lrapopwitness = Nid::ID_CMC_LRAPOPWITNESS; + let ms_ctl_sign = Nid::MS_CTL_SIGN; + let undefined_nid = Nid::from_raw(118); + + assert_eq!(common_name.long_name().unwrap(), "commonName"); + assert_eq!( + organizational_unit_name.long_name().unwrap(), + "organizationalUnitName" + ); + assert_eq!( + aes256_cbc_hmac_sha1.long_name().unwrap(), + "aes-256-cbc-hmac-sha1" + ); + assert_eq!( + id_cmc_lrapopwitness.long_name().unwrap(), + "id-cmc-lraPOPWitness" + ); + assert_eq!( + ms_ctl_sign.long_name().unwrap(), + "Microsoft Trust List Signing" + ); + assert!( + undefined_nid.long_name().is_err(), + "undefined_nid should not return a valid value" + ); + } + + #[test] + fn test_short_name_conversion() { + let common_name = Nid::COMMONNAME; + let organizational_unit_name = Nid::ORGANIZATIONALUNITNAME; + let aes256_cbc_hmac_sha1 = Nid::AES_256_CBC_HMAC_SHA1; + let id_cmc_lrapopwitness = Nid::ID_CMC_LRAPOPWITNESS; + let ms_ctl_sign = Nid::MS_CTL_SIGN; + let undefined_nid = Nid::from_raw(118); + + assert_eq!(common_name.short_name().unwrap(), "CN"); + assert_eq!(organizational_unit_name.short_name().unwrap(), "OU"); + assert_eq!( + aes256_cbc_hmac_sha1.short_name().unwrap(), + "AES-256-CBC-HMAC-SHA1" + ); + assert_eq!( + id_cmc_lrapopwitness.short_name().unwrap(), + "id-cmc-lraPOPWitness" + ); + assert_eq!(ms_ctl_sign.short_name().unwrap(), "msCTLSign"); + assert!( + undefined_nid.short_name().is_err(), + "undefined_nid should not return a valid value" + ); + } +} diff --git a/crates/boring/src/pkcs12.rs b/crates/boring/src/pkcs12.rs new file mode 100644 index 000000000..4caec029a --- /dev/null +++ b/crates/boring/src/pkcs12.rs @@ -0,0 +1,287 @@ +//! PKCS #12 archives. + +use crate::ffi; +use foreign_types::{ForeignType, ForeignTypeRef}; +use libc::c_int; +use std::ffi::CString; +use std::ptr; + +use crate::error::ErrorStack; +use crate::nid::Nid; +use crate::pkey::{HasPrivate, PKey, PKeyRef, Private}; +use crate::stack::Stack; +use crate::x509::{X509Ref, X509}; +use crate::{cvt_0i, cvt_p}; + +pub const PKCS12_DEFAULT_ITER: c_int = 2048; + +foreign_type_and_impl_send_sync! { + type CType = ffi::PKCS12; + fn drop = ffi::PKCS12_free; + + pub struct Pkcs12; +} + +impl Pkcs12Ref { + to_der! { + /// Serializes the `Pkcs12` to its standard DER encoding. + /// + /// This corresponds to [`i2d_PKCS12`]. + /// + /// [`i2d_PKCS12`]: https://www.openssl.org/docs/manmaster/man3/i2d_PKCS12.html + to_der, + ffi::i2d_PKCS12 + } + + /// Extracts the contents of the `Pkcs12`. + pub fn parse(&self, pass: &str) -> Result { + unsafe { + let pass = CString::new(pass.as_bytes()).unwrap(); + + let mut pkey = ptr::null_mut(); + let mut cert = ptr::null_mut(); + let mut chain = ptr::null_mut(); + + cvt_0i(ffi::PKCS12_parse( + self.as_ptr(), + pass.as_ptr(), + &mut pkey, + &mut cert, + &mut chain, + ))?; + + let pkey = PKey::from_ptr(pkey); + let cert = X509::from_ptr(cert); + + let chain = if chain.is_null() { + None + } else { + Some(Stack::from_ptr(chain)) + }; + + Ok(ParsedPkcs12 { pkey, cert, chain }) + } + } +} + +impl Pkcs12 { + from_der! { + /// Deserializes a DER-encoded PKCS#12 archive. + /// + /// This corresponds to [`d2i_PKCS12`]. + /// + /// [`d2i_PKCS12`]: https://www.openssl.org/docs/man1.1.0/crypto/d2i_PKCS12.html + from_der, + Pkcs12, + ffi::d2i_PKCS12, + ::libc::size_t + } + + /// Creates a new builder for a protected pkcs12 certificate. + /// + /// This uses the defaults from the OpenSSL library: + /// + /// * `nid_key` - `nid::PBE_WITHSHA1AND3_KEY_TRIPLEDES_CBC` + /// * `nid_cert` - `nid::PBE_WITHSHA1AND40BITRC2_CBC` + /// * `iter` - `2048` + /// * `mac_iter` - `2048` + pub fn builder() -> Pkcs12Builder { + ffi::init(); + + Pkcs12Builder { + nid_key: Nid::UNDEF, //nid::PBE_WITHSHA1AND3_KEY_TRIPLEDES_CBC, + nid_cert: Nid::UNDEF, //nid::PBE_WITHSHA1AND40BITRC2_CBC, + iter: PKCS12_DEFAULT_ITER, + mac_iter: PKCS12_DEFAULT_ITER, + ca: None, + } + } +} + +pub struct ParsedPkcs12 { + pub pkey: PKey, + pub cert: X509, + pub chain: Option>, +} + +pub struct Pkcs12Builder { + nid_key: Nid, + nid_cert: Nid, + iter: c_int, + mac_iter: c_int, + ca: Option>, +} + +impl Pkcs12Builder { + /// The encryption algorithm that should be used for the key + pub fn key_algorithm(&mut self, nid: Nid) -> &mut Self { + self.nid_key = nid; + self + } + + /// The encryption algorithm that should be used for the cert + pub fn cert_algorithm(&mut self, nid: Nid) -> &mut Self { + self.nid_cert = nid; + self + } + + /// Key iteration count, default is 2048 as of this writing + pub fn key_iter(&mut self, iter: u32) -> &mut Self { + self.iter = iter as c_int; + self + } + + /// MAC iteration count, default is the same as key_iter. + /// + /// Old implementations don't understand MAC iterations greater than 1, (pre 1.0.1?), if such + /// compatibility is required this should be set to 1. + pub fn mac_iter(&mut self, mac_iter: u32) -> &mut Self { + self.mac_iter = mac_iter as c_int; + self + } + + /// An additional set of certificates to include in the archive beyond the one provided to + /// `build`. + pub fn ca(&mut self, ca: Stack) -> &mut Self { + self.ca = Some(ca); + self + } + + /// Builds the PKCS #12 object + /// + /// # Arguments + /// + /// * `password` - the password used to encrypt the key and certificate + /// * `friendly_name` - user defined name for the certificate + /// * `pkey` - key to store + /// * `cert` - certificate to store + pub fn build( + self, + password: &str, + friendly_name: &str, + pkey: &PKeyRef, + cert: &X509Ref, + ) -> Result + where + T: HasPrivate, + { + unsafe { + let pass = CString::new(password).unwrap(); + let friendly_name = CString::new(friendly_name).unwrap(); + let pkey = pkey.as_ptr(); + let cert = cert.as_ptr(); + let ca = self + .ca + .as_ref() + .map(|ca| ca.as_ptr()) + .unwrap_or(ptr::null_mut()); + let nid_key = self.nid_key.as_raw(); + let nid_cert = self.nid_cert.as_raw(); + + // According to the OpenSSL docs, keytype is a non-standard extension for MSIE, + // It's values are KEY_SIG or KEY_EX, see the OpenSSL docs for more information: + // https://www.openssl.org/docs/man1.0.2/crypto/PKCS12_create.html + let keytype = 0; + + cvt_p(ffi::PKCS12_create( + pass.as_ptr() as *const _ as *mut _, + friendly_name.as_ptr() as *const _ as *mut _, + pkey, + cert, + ca, + nid_key, + nid_cert, + self.iter, + self.mac_iter, + keytype, + )) + .map(|p| Pkcs12::from_ptr(p)) + } + } +} + +#[cfg(test)] +mod test { + use crate::hash::MessageDigest; + use hex; + + use crate::asn1::Asn1Time; + use crate::nid::Nid; + use crate::pkey::PKey; + use crate::rsa::Rsa; + use crate::x509::extension::KeyUsage; + use crate::x509::{X509Name, X509}; + + use super::*; + + #[test] + fn parse() { + let der = include_bytes!("../test/identity.p12"); + let pkcs12 = Pkcs12::from_der(der).unwrap(); + let parsed = pkcs12.parse("mypass").unwrap(); + + assert_eq!( + hex::encode(parsed.cert.digest(MessageDigest::sha1()).unwrap()), + "59172d9313e84459bcff27f967e79e6e9217e584" + ); + + let chain = parsed.chain.unwrap(); + assert_eq!(chain.len(), 1); + assert_eq!( + hex::encode(chain[0].digest(MessageDigest::sha1()).unwrap()), + "c0cbdf7cdd03c9773e5468e1f6d2da7d5cbb1875" + ); + } + + #[test] + fn parse_empty_chain() { + let der = include_bytes!("../test/keystore-empty-chain.p12"); + let pkcs12 = Pkcs12::from_der(der).unwrap(); + let parsed = pkcs12.parse("cassandra").unwrap(); + assert_eq!(parsed.chain.unwrap().len(), 0); + } + + #[test] + fn create() { + let subject_name = "ns.example.com"; + let rsa = Rsa::generate(2048).unwrap(); + let pkey = PKey::from_rsa(rsa).unwrap(); + + let mut name = X509Name::builder().unwrap(); + name.append_entry_by_nid(Nid::COMMONNAME, subject_name) + .unwrap(); + let name = name.build(); + + let key_usage = KeyUsage::new().digital_signature().build().unwrap(); + + let mut builder = X509::builder().unwrap(); + builder.set_version(2).unwrap(); + builder + .set_not_before(&Asn1Time::days_from_now(0).unwrap()) + .unwrap(); + builder + .set_not_after(&Asn1Time::days_from_now(365).unwrap()) + .unwrap(); + builder.set_subject_name(&name).unwrap(); + builder.set_issuer_name(&name).unwrap(); + builder.append_extension(key_usage).unwrap(); + builder.set_pubkey(&pkey).unwrap(); + builder.sign(&pkey, MessageDigest::sha256()).unwrap(); + let cert = builder.build(); + + let pkcs12_builder = Pkcs12::builder(); + let pkcs12 = pkcs12_builder + .build("mypass", subject_name, &pkey, &cert) + .unwrap(); + let der = pkcs12.to_der().unwrap(); + + let pkcs12 = Pkcs12::from_der(&der).unwrap(); + let parsed = pkcs12.parse("mypass").unwrap(); + + assert_eq!( + &*parsed.cert.digest(MessageDigest::sha1()).unwrap(), + &*cert.digest(MessageDigest::sha1()).unwrap() + ); + assert!(parsed.pkey.public_eq(&pkey)); + } +} diff --git a/crates/boring/src/pkcs5.rs b/crates/boring/src/pkcs5.rs new file mode 100644 index 000000000..8503d0b34 --- /dev/null +++ b/crates/boring/src/pkcs5.rs @@ -0,0 +1,302 @@ +use crate::ffi; +use libc::{c_int, c_uint}; +use std::ptr; + +use crate::cvt; +use crate::error::ErrorStack; +use crate::hash::MessageDigest; +use crate::symm::Cipher; + +#[derive(Clone, Eq, PartialEq, Hash, Debug)] +pub struct KeyIvPair { + pub key: Vec, + pub iv: Option>, +} + +/// Derives a key and an IV from various parameters. +/// +/// If specified, `salt` must be 8 bytes in length. +/// +/// If the total key and IV length is less than 16 bytes and MD5 is used then +/// the algorithm is compatible with the key derivation algorithm from PKCS#5 +/// v1.5 or PBKDF1 from PKCS#5 v2.0. +/// +/// New applications should not use this and instead use +/// `pbkdf2_hmac` or another more modern key derivation algorithm. +#[allow(clippy::useless_conversion)] +pub fn bytes_to_key( + cipher: Cipher, + digest: MessageDigest, + data: &[u8], + salt: Option<&[u8]>, + count: u32, +) -> Result { + unsafe { + assert!(data.len() <= c_int::max_value() as usize); + let salt_ptr = match salt { + Some(salt) => { + pub const PKCS5_SALT_LEN: c_int = 8; + assert_eq!(salt.len(), PKCS5_SALT_LEN as usize); + salt.as_ptr() + } + None => ptr::null(), + }; + + ffi::init(); + + let mut iv = cipher.iv_len().map(|l| vec![0; l]); + + let cipher = cipher.as_ptr(); + let digest = digest.as_ptr(); + + let len = cvt(ffi::EVP_BytesToKey( + cipher, + digest, + salt_ptr, + ptr::null(), + data.len(), + count, + ptr::null_mut(), + ptr::null_mut(), + ))?; + + let mut key = vec![0; len as usize]; + let iv_ptr = iv + .as_mut() + .map(|v| v.as_mut_ptr()) + .unwrap_or(ptr::null_mut()); + + cvt(ffi::EVP_BytesToKey( + cipher, + digest, + salt_ptr, + data.as_ptr(), + data.len(), + count, + key.as_mut_ptr(), + iv_ptr, + ))?; + + Ok(KeyIvPair { key, iv }) + } +} + +/// Derives a key from a password and salt using the PBKDF2-HMAC algorithm with a digest function. +pub fn pbkdf2_hmac( + pass: &[u8], + salt: &[u8], + iter: usize, + hash: MessageDigest, + key: &mut [u8], +) -> Result<(), ErrorStack> { + unsafe { + assert!(pass.len() <= c_int::max_value() as usize); + assert!(salt.len() <= c_int::max_value() as usize); + assert!(key.len() <= c_int::max_value() as usize); + + ffi::init(); + cvt(ffi::PKCS5_PBKDF2_HMAC( + pass.as_ptr() as *const _, + pass.len(), + salt.as_ptr(), + salt.len(), + iter as c_uint, + hash.as_ptr(), + key.len(), + key.as_mut_ptr(), + )) + .map(|_| ()) + } +} + +/// Derives a key from a password and salt using the scrypt algorithm. +pub fn scrypt( + pass: &[u8], + salt: &[u8], + n: u64, + r: u64, + p: u64, + maxmem: usize, + key: &mut [u8], +) -> Result<(), ErrorStack> { + unsafe { + ffi::init(); + cvt(ffi::EVP_PBE_scrypt( + pass.as_ptr() as *const _, + pass.len(), + salt.as_ptr() as *const _, + salt.len(), + n, + r, + p, + maxmem, + key.as_mut_ptr() as *mut _, + key.len(), + )) + .map(|_| ()) + } +} + +#[cfg(test)] +mod tests { + use crate::hash::MessageDigest; + use crate::symm::Cipher; + + // Test vectors from + // https://git.lysator.liu.se/nettle/nettle/blob/nettle_3.1.1_release_20150424/testsuite/pbkdf2-test.c + #[test] + fn pbkdf2_hmac_sha256() { + let mut buf = [0; 16]; + + super::pbkdf2_hmac(b"passwd", b"salt", 1, MessageDigest::sha256(), &mut buf).unwrap(); + assert_eq!( + buf, + &[ + 0x55_u8, 0xac_u8, 0x04_u8, 0x6e_u8, 0x56_u8, 0xe3_u8, 0x08_u8, 0x9f_u8, 0xec_u8, + 0x16_u8, 0x91_u8, 0xc2_u8, 0x25_u8, 0x44_u8, 0xb6_u8, 0x05_u8, + ][..] + ); + + super::pbkdf2_hmac( + b"Password", + b"NaCl", + 80000, + MessageDigest::sha256(), + &mut buf, + ) + .unwrap(); + assert_eq!( + buf, + &[ + 0x4d_u8, 0xdc_u8, 0xd8_u8, 0xf6_u8, 0x0b_u8, 0x98_u8, 0xbe_u8, 0x21_u8, 0x83_u8, + 0x0c_u8, 0xee_u8, 0x5e_u8, 0xf2_u8, 0x27_u8, 0x01_u8, 0xf9_u8, + ][..] + ); + } + + // Test vectors from + // https://git.lysator.liu.se/nettle/nettle/blob/nettle_3.1.1_release_20150424/testsuite/pbkdf2-test.c + #[test] + fn pbkdf2_hmac_sha512() { + let mut buf = [0; 64]; + + super::pbkdf2_hmac(b"password", b"NaCL", 1, MessageDigest::sha512(), &mut buf).unwrap(); + assert_eq!( + &buf[..], + &[ + 0x73_u8, 0xde_u8, 0xcf_u8, 0xa5_u8, 0x8a_u8, 0xa2_u8, 0xe8_u8, 0x4f_u8, 0x94_u8, + 0x77_u8, 0x1a_u8, 0x75_u8, 0x73_u8, 0x6b_u8, 0xb8_u8, 0x8b_u8, 0xd3_u8, 0xc7_u8, + 0xb3_u8, 0x82_u8, 0x70_u8, 0xcf_u8, 0xb5_u8, 0x0c_u8, 0xb3_u8, 0x90_u8, 0xed_u8, + 0x78_u8, 0xb3_u8, 0x05_u8, 0x65_u8, 0x6a_u8, 0xf8_u8, 0x14_u8, 0x8e_u8, 0x52_u8, + 0x45_u8, 0x2b_u8, 0x22_u8, 0x16_u8, 0xb2_u8, 0xb8_u8, 0x09_u8, 0x8b_u8, 0x76_u8, + 0x1f_u8, 0xc6_u8, 0x33_u8, 0x60_u8, 0x60_u8, 0xa0_u8, 0x9f_u8, 0x76_u8, 0x41_u8, + 0x5e_u8, 0x9f_u8, 0x71_u8, 0xea_u8, 0x47_u8, 0xf9_u8, 0xe9_u8, 0x06_u8, 0x43_u8, + 0x06_u8, + ][..] + ); + + super::pbkdf2_hmac( + b"pass\0word", + b"sa\0lt", + 1, + MessageDigest::sha512(), + &mut buf, + ) + .unwrap(); + assert_eq!( + &buf[..], + &[ + 0x71_u8, 0xa0_u8, 0xec_u8, 0x84_u8, 0x2a_u8, 0xbd_u8, 0x5c_u8, 0x67_u8, 0x8b_u8, + 0xcf_u8, 0xd1_u8, 0x45_u8, 0xf0_u8, 0x9d_u8, 0x83_u8, 0x52_u8, 0x2f_u8, 0x93_u8, + 0x36_u8, 0x15_u8, 0x60_u8, 0x56_u8, 0x3c_u8, 0x4d_u8, 0x0d_u8, 0x63_u8, 0xb8_u8, + 0x83_u8, 0x29_u8, 0x87_u8, 0x10_u8, 0x90_u8, 0xe7_u8, 0x66_u8, 0x04_u8, 0xa4_u8, + 0x9a_u8, 0xf0_u8, 0x8f_u8, 0xe7_u8, 0xc9_u8, 0xf5_u8, 0x71_u8, 0x56_u8, 0xc8_u8, + 0x79_u8, 0x09_u8, 0x96_u8, 0xb2_u8, 0x0f_u8, 0x06_u8, 0xbc_u8, 0x53_u8, 0x5e_u8, + 0x5a_u8, 0xb5_u8, 0x44_u8, 0x0d_u8, 0xf7_u8, 0xe8_u8, 0x78_u8, 0x29_u8, 0x6f_u8, + 0xa7_u8, + ][..] + ); + + super::pbkdf2_hmac( + b"passwordPASSWORDpassword", + b"salt\0\0\0", + 50, + MessageDigest::sha512(), + &mut buf, + ) + .unwrap(); + assert_eq!( + &buf[..], + &[ + 0x01_u8, 0x68_u8, 0x71_u8, 0xa4_u8, 0xc4_u8, 0xb7_u8, 0x5f_u8, 0x96_u8, 0x85_u8, + 0x7f_u8, 0xd2_u8, 0xb9_u8, 0xf8_u8, 0xca_u8, 0x28_u8, 0x02_u8, 0x3b_u8, 0x30_u8, + 0xee_u8, 0x2a_u8, 0x39_u8, 0xf5_u8, 0xad_u8, 0xca_u8, 0xc8_u8, 0xc9_u8, 0x37_u8, + 0x5f_u8, 0x9b_u8, 0xda_u8, 0x1c_u8, 0xcd_u8, 0x1b_u8, 0x6f_u8, 0x0b_u8, 0x2f_u8, + 0xc3_u8, 0xad_u8, 0xda_u8, 0x50_u8, 0x54_u8, 0x12_u8, 0xe7_u8, 0x9d_u8, 0x89_u8, + 0x00_u8, 0x56_u8, 0xc6_u8, 0x2e_u8, 0x52_u8, 0x4c_u8, 0x7d_u8, 0x51_u8, 0x15_u8, + 0x4b_u8, 0x1a_u8, 0x85_u8, 0x34_u8, 0x57_u8, 0x5b_u8, 0xd0_u8, 0x2d_u8, 0xee_u8, + 0x39_u8, + ][..] + ); + } + + #[test] + fn bytes_to_key() { + let salt = [16_u8, 34_u8, 19_u8, 23_u8, 141_u8, 4_u8, 207_u8, 221_u8]; + + let data = [ + 143_u8, 210_u8, 75_u8, 63_u8, 214_u8, 179_u8, 155_u8, 241_u8, 242_u8, 31_u8, 154_u8, + 56_u8, 198_u8, 145_u8, 192_u8, 64_u8, 2_u8, 245_u8, 167_u8, 220_u8, 55_u8, 119_u8, + 233_u8, 136_u8, 139_u8, 27_u8, 71_u8, 242_u8, 119_u8, 175_u8, 65_u8, 207_u8, + ]; + + let expected_key = vec![ + 249_u8, 115_u8, 114_u8, 97_u8, 32_u8, 213_u8, 165_u8, 146_u8, 58_u8, 87_u8, 234_u8, + 3_u8, 43_u8, 250_u8, 97_u8, 114_u8, 26_u8, 98_u8, 245_u8, 246_u8, 238_u8, 177_u8, + 229_u8, 161_u8, 183_u8, 224_u8, 174_u8, 3_u8, 6_u8, 244_u8, 236_u8, 255_u8, + ]; + let expected_iv = vec![ + 4_u8, 223_u8, 153_u8, 219_u8, 28_u8, 142_u8, 234_u8, 68_u8, 227_u8, 69_u8, 98_u8, + 107_u8, 208_u8, 14_u8, 236_u8, 60_u8, + ]; + + assert_eq!( + super::bytes_to_key( + Cipher::aes_256_cbc(), + MessageDigest::sha1(), + &data, + Some(&salt), + 1, + ) + .unwrap(), + super::KeyIvPair { + key: expected_key, + iv: Some(expected_iv), + } + ); + } + + #[test] + fn scrypt() { + use hex; + + let pass = "pleaseletmein"; + let salt = "SodiumChloride"; + let expected = + "7023bdcb3afd7348461c06cd81fd38ebfda8fbba904f8e3ea9b543f6545da1f2d5432955613\ + f0fcf62d49705242a9af9e61e85dc0d651e40dfcf017b45575887"; + + let mut actual = [0; 64]; + super::scrypt( + pass.as_bytes(), + salt.as_bytes(), + 16384, + 8, + 1, + 0, + &mut actual, + ) + .unwrap(); + assert_eq!(hex::encode(&actual[..]), expected); + } +} diff --git a/crates/boring/src/pkey.rs b/crates/boring/src/pkey.rs new file mode 100644 index 000000000..0975b3955 --- /dev/null +++ b/crates/boring/src/pkey.rs @@ -0,0 +1,618 @@ +//! Public/private key processing. +//! +//! Asymmetric public key algorithms solve the problem of establishing and sharing +//! secret keys to securely send and receive messages. +//! This system uses a pair of keys: a public key, which can be freely +//! distributed, and a private key, which is kept to oneself. An entity may +//! encrypt information using a user's public key. The encrypted information can +//! only be deciphered using that user's private key. +//! +//! This module offers support for five popular algorithms: +//! +//! * RSA +//! +//! * DSA +//! +//! * Diffie-Hellman +//! +//! * Elliptic Curves +//! +//! * HMAC +//! +//! These algorithms rely on hard mathematical problems - namely integer factorization, +//! discrete logarithms, and elliptic curve relationships - that currently do not +//! yield efficient solutions. This property ensures the security of these +//! cryptographic algorithms. +//! +//! # Example +//! +//! Generate a 2048-bit RSA public/private key pair and print the public key. +//! +//! ```rust +//! use boring::rsa::Rsa; +//! use boring::pkey::PKey; +//! use std::str; +//! +//! let rsa = Rsa::generate(2048).unwrap(); +//! let pkey = PKey::from_rsa(rsa).unwrap(); +//! +//! let pub_key: Vec = pkey.public_key_to_pem().unwrap(); +//! println!("{:?}", str::from_utf8(pub_key.as_slice()).unwrap()); +//! ``` + +use crate::ffi; +use foreign_types::{ForeignType, ForeignTypeRef}; +use libc::{c_int, c_long}; +use std::ffi::CString; +use std::fmt; +use std::mem; +use std::ptr; + +use crate::bio::MemBioSlice; +use crate::dh::Dh; +use crate::dsa::Dsa; +use crate::ec::EcKey; +use crate::error::ErrorStack; +use crate::rsa::Rsa; +use crate::util::{invoke_passwd_cb, CallbackState}; +use crate::{cvt, cvt_p}; + +/// A tag type indicating that a key only has parameters. +pub enum Params {} + +/// A tag type indicating that a key only has public components. +pub enum Public {} + +/// A tag type indicating that a key has private components. +pub enum Private {} + +/// An identifier of a kind of key. +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +pub struct Id(c_int); + +impl Id { + pub const RSA: Id = Id(ffi::EVP_PKEY_RSA); + pub const DSA: Id = Id(ffi::EVP_PKEY_DSA); + pub const DH: Id = Id(ffi::EVP_PKEY_DH); + pub const EC: Id = Id(ffi::EVP_PKEY_EC); + pub const ED25519: Id = Id(ffi::EVP_PKEY_ED25519); + pub const ED448: Id = Id(ffi::EVP_PKEY_ED448); + pub const X25519: Id = Id(ffi::EVP_PKEY_X25519); + pub const X448: Id = Id(ffi::EVP_PKEY_X448); + + /// Creates a `Id` from an integer representation. + pub fn from_raw(value: c_int) -> Id { + Id(value) + } + + /// Returns the integer representation of the `Id`. + #[allow(clippy::trivially_copy_pass_by_ref)] + pub fn as_raw(&self) -> c_int { + self.0 + } +} + +/// A trait indicating that a key has parameters. +#[allow(clippy::missing_safety_doc)] +pub unsafe trait HasParams {} + +unsafe impl HasParams for Params {} + +unsafe impl HasParams for T where T: HasPublic {} + +/// A trait indicating that a key has public components. +#[allow(clippy::missing_safety_doc)] +pub unsafe trait HasPublic {} + +unsafe impl HasPublic for Public {} + +unsafe impl HasPublic for T where T: HasPrivate {} + +/// A trait indicating that a key has private components. +#[allow(clippy::missing_safety_doc)] +pub unsafe trait HasPrivate {} + +unsafe impl HasPrivate for Private {} + +generic_foreign_type_and_impl_send_sync! { + type CType = ffi::EVP_PKEY; + fn drop = ffi::EVP_PKEY_free; + + /// A public or private key. + pub struct PKey; + /// Reference to [`PKey`]. + pub struct PKeyRef; +} + +impl ToOwned for PKeyRef { + type Owned = PKey; + + fn to_owned(&self) -> PKey { + unsafe { + EVP_PKEY_up_ref(self.as_ptr()); + PKey::from_ptr(self.as_ptr()) + } + } +} + +impl PKeyRef { + /// Returns a copy of the internal RSA key. + /// + /// This corresponds to [`EVP_PKEY_get1_RSA`]. + /// + /// [`EVP_PKEY_get1_RSA`]: https://www.openssl.org/docs/man1.1.0/crypto/EVP_PKEY_get1_RSA.html + pub fn rsa(&self) -> Result, ErrorStack> { + unsafe { + let rsa = cvt_p(ffi::EVP_PKEY_get1_RSA(self.as_ptr()))?; + Ok(Rsa::from_ptr(rsa)) + } + } + + /// Returns a copy of the internal DSA key. + /// + /// This corresponds to [`EVP_PKEY_get1_DSA`]. + /// + /// [`EVP_PKEY_get1_DSA`]: https://www.openssl.org/docs/man1.1.0/crypto/EVP_PKEY_get1_DSA.html + pub fn dsa(&self) -> Result, ErrorStack> { + unsafe { + let dsa = cvt_p(ffi::EVP_PKEY_get1_DSA(self.as_ptr()))?; + Ok(Dsa::from_ptr(dsa)) + } + } + + /// Returns a copy of the internal DH key. + /// + /// This corresponds to [`EVP_PKEY_get1_DH`]. + /// + /// [`EVP_PKEY_get1_DH`]: https://www.openssl.org/docs/man1.1.0/crypto/EVP_PKEY_get1_DH.html + pub fn dh(&self) -> Result, ErrorStack> { + unsafe { + let dh = cvt_p(ffi::EVP_PKEY_get1_DH(self.as_ptr()))?; + Ok(Dh::from_ptr(dh)) + } + } + + /// Returns a copy of the internal elliptic curve key. + /// + /// This corresponds to [`EVP_PKEY_get1_EC_KEY`]. + /// + /// [`EVP_PKEY_get1_EC_KEY`]: https://www.openssl.org/docs/man1.1.0/crypto/EVP_PKEY_get1_EC_KEY.html + pub fn ec_key(&self) -> Result, ErrorStack> { + unsafe { + let ec_key = cvt_p(ffi::EVP_PKEY_get1_EC_KEY(self.as_ptr()))?; + Ok(EcKey::from_ptr(ec_key)) + } + } + + /// Returns the `Id` that represents the type of this key. + /// + /// This corresponds to [`EVP_PKEY_id`]. + /// + /// [`EVP_PKEY_id`]: https://www.openssl.org/docs/man1.1.0/crypto/EVP_PKEY_id.html + pub fn id(&self) -> Id { + unsafe { Id::from_raw(ffi::EVP_PKEY_id(self.as_ptr())) } + } + + /// Returns the maximum size of a signature in bytes. + /// + /// This corresponds to [`EVP_PKEY_size`]. + /// + /// [`EVP_PKEY_size`]: https://www.openssl.org/docs/man1.1.1/man3/EVP_PKEY_size.html + pub fn size(&self) -> usize { + unsafe { ffi::EVP_PKEY_size(self.as_ptr()) as usize } + } +} + +impl PKeyRef +where + T: HasPublic, +{ + to_pem! { + /// Serializes the public key into a PEM-encoded SubjectPublicKeyInfo structure. + /// + /// The output will have a header of `-----BEGIN PUBLIC KEY-----`. + /// + /// This corresponds to [`PEM_write_bio_PUBKEY`]. + /// + /// [`PEM_write_bio_PUBKEY`]: https://www.openssl.org/docs/man1.1.0/crypto/PEM_write_bio_PUBKEY.html + public_key_to_pem, + ffi::PEM_write_bio_PUBKEY + } + + to_der! { + /// Serializes the public key into a DER-encoded SubjectPublicKeyInfo structure. + /// + /// This corresponds to [`i2d_PUBKEY`]. + /// + /// [`i2d_PUBKEY`]: https://www.openssl.org/docs/man1.1.0/crypto/i2d_PUBKEY.html + public_key_to_der, + ffi::i2d_PUBKEY + } + + /// Returns the size of the key. + /// + /// This corresponds to the bit length of the modulus of an RSA key, and the bit length of the + /// group order for an elliptic curve key, for example. + pub fn bits(&self) -> u32 { + unsafe { ffi::EVP_PKEY_bits(self.as_ptr()) as u32 } + } + + /// Compares the public component of this key with another. + pub fn public_eq(&self, other: &PKeyRef) -> bool + where + U: HasPublic, + { + unsafe { ffi::EVP_PKEY_cmp(self.as_ptr(), other.as_ptr()) == 1 } + } +} + +impl PKeyRef +where + T: HasPrivate, +{ + private_key_to_pem! { + /// Serializes the private key to a PEM-encoded PKCS#8 PrivateKeyInfo structure. + /// + /// The output will have a header of `-----BEGIN PRIVATE KEY-----`. + /// + /// This corresponds to [`PEM_write_bio_PKCS8PrivateKey`]. + /// + /// [`PEM_write_bio_PKCS8PrivateKey`]: https://www.openssl.org/docs/man1.0.2/crypto/PEM_write_bio_PKCS8PrivateKey.html + private_key_to_pem_pkcs8, + /// Serializes the private key to a PEM-encoded PKCS#8 EncryptedPrivateKeyInfo structure. + /// + /// The output will have a header of `-----BEGIN ENCRYPTED PRIVATE KEY-----`. + /// + /// This corresponds to [`PEM_write_bio_PKCS8PrivateKey`]. + /// + /// [`PEM_write_bio_PKCS8PrivateKey`]: https://www.openssl.org/docs/man1.0.2/crypto/PEM_write_bio_PKCS8PrivateKey.html + private_key_to_pem_pkcs8_passphrase, + ffi::PEM_write_bio_PKCS8PrivateKey + } + + to_der! { + /// Serializes the private key to a DER-encoded key type specific format. + /// + /// This corresponds to [`i2d_PrivateKey`]. + /// + /// [`i2d_PrivateKey`]: https://www.openssl.org/docs/man1.0.2/crypto/i2d_PrivateKey.html + private_key_to_der, + ffi::i2d_PrivateKey + } + + // This isn't actually PEM output, but `i2d_PKCS8PrivateKey_bio` is documented to be + // "identical to the corresponding PEM function", and it's declared in pem.h. + private_key_to_pem! { + /// Serializes the private key to a DER-encoded PKCS#8 PrivateKeyInfo structure. + /// + /// This corresponds to [`i2d_PKCS8PrivateKey_bio`]. + /// + /// [`i2d_PKCS8PrivateKey_bio`]: https://www.openssl.org/docs/man1.1.1/man3/i2d_PKCS8PrivateKey_bio.html + private_key_to_der_pkcs8, + /// Serializes the private key to a DER-encoded PKCS#8 EncryptedPrivateKeyInfo structure. + /// + /// This corresponds to [`i2d_PKCS8PrivateKey_bio`]. + /// + /// [`i2d_PKCS8PrivateKey_bio`]: https://www.openssl.org/docs/man1.1.1/man3/i2d_PKCS8PrivateKey_bio.html + private_key_to_der_pkcs8_passphrase, + ffi::i2d_PKCS8PrivateKey_bio + } +} + +impl fmt::Debug for PKey { + fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { + let alg = match self.id() { + Id::RSA => "RSA", + Id::DSA => "DSA", + Id::DH => "DH", + Id::EC => "EC", + Id::ED25519 => "Ed25519", + Id::ED448 => "Ed448", + _ => "unknown", + }; + fmt.debug_struct("PKey").field("algorithm", &alg).finish() + // TODO: Print details for each specific type of key + } +} + +impl Clone for PKey { + fn clone(&self) -> PKey { + PKeyRef::to_owned(self) + } +} + +impl PKey { + /// Creates a new `PKey` containing an RSA key. + /// + /// This corresponds to [`EVP_PKEY_assign_RSA`]. + /// + /// [`EVP_PKEY_assign_RSA`]: https://www.openssl.org/docs/man1.1.0/crypto/EVP_PKEY_assign_RSA.html + pub fn from_rsa(rsa: Rsa) -> Result, ErrorStack> { + unsafe { + let evp = cvt_p(ffi::EVP_PKEY_new())?; + let pkey = PKey::from_ptr(evp); + cvt(ffi::EVP_PKEY_assign( + pkey.0, + ffi::EVP_PKEY_RSA, + rsa.as_ptr() as *mut _, + ))?; + mem::forget(rsa); + Ok(pkey) + } + } + + /// Creates a new `PKey` containing an elliptic curve key. + /// + /// This corresponds to [`EVP_PKEY_assign_EC_KEY`]. + /// + /// [`EVP_PKEY_assign_EC_KEY`]: https://www.openssl.org/docs/man1.1.0/crypto/EVP_PKEY_assign_EC_KEY.html + pub fn from_ec_key(ec_key: EcKey) -> Result, ErrorStack> { + unsafe { + let evp = cvt_p(ffi::EVP_PKEY_new())?; + let pkey = PKey::from_ptr(evp); + cvt(ffi::EVP_PKEY_assign( + pkey.0, + ffi::EVP_PKEY_EC, + ec_key.as_ptr() as *mut _, + ))?; + mem::forget(ec_key); + Ok(pkey) + } + } +} + +impl PKey { + private_key_from_pem! { + /// Deserializes a private key from a PEM-encoded key type specific format. + /// + /// This corresponds to [`PEM_read_bio_PrivateKey`]. + /// + /// [`PEM_read_bio_PrivateKey`]: https://www.openssl.org/docs/man1.1.0/crypto/PEM_read_bio_PrivateKey.html + private_key_from_pem, + + /// Deserializes a private key from a PEM-encoded encrypted key type specific format. + /// + /// This corresponds to [`PEM_read_bio_PrivateKey`]. + /// + /// [`PEM_read_bio_PrivateKey`]: https://www.openssl.org/docs/man1.1.0/crypto/PEM_read_bio_PrivateKey.html + private_key_from_pem_passphrase, + + /// Deserializes a private key from a PEM-encoded encrypted key type specific format. + /// + /// The callback should fill the password into the provided buffer and return its length. + /// + /// This corresponds to [`PEM_read_bio_PrivateKey`]. + /// + /// [`PEM_read_bio_PrivateKey`]: https://www.openssl.org/docs/man1.1.0/crypto/PEM_read_bio_PrivateKey.html + private_key_from_pem_callback, + PKey, + ffi::PEM_read_bio_PrivateKey + } + + from_der! { + /// Decodes a DER-encoded private key. + /// + /// This function will automatically attempt to detect the underlying key format, and + /// supports the unencrypted PKCS#8 PrivateKeyInfo structures as well as key type specific + /// formats. + /// + /// This corresponds to [`d2i_AutoPrivateKey`]. + /// + /// [`d2i_AutoPrivateKey`]: https://www.openssl.org/docs/man1.0.2/crypto/d2i_AutoPrivateKey.html + private_key_from_der, + PKey, + ffi::d2i_AutoPrivateKey, + ::libc::c_long + } + + /// Deserializes a DER-formatted PKCS#8 unencrypted private key. + /// + /// This method is mainly for interoperability reasons. Encrypted keyfiles should be preferred. + pub fn private_key_from_pkcs8(der: &[u8]) -> Result, ErrorStack> { + unsafe { + ffi::init(); + let len = der.len().min(c_long::max_value() as usize) as c_long; + let p8inf = cvt_p(ffi::d2i_PKCS8_PRIV_KEY_INFO( + ptr::null_mut(), + &mut der.as_ptr(), + len, + ))?; + let res = cvt_p(ffi::EVP_PKCS82PKEY(p8inf)).map(|p| PKey::from_ptr(p)); + ffi::PKCS8_PRIV_KEY_INFO_free(p8inf); + res + } + } + + /// Deserializes a DER-formatted PKCS#8 private key, using a callback to retrieve the password + /// if the key is encrpyted. + /// + /// The callback should copy the password into the provided buffer and return the number of + /// bytes written. + pub fn private_key_from_pkcs8_callback( + der: &[u8], + callback: F, + ) -> Result, ErrorStack> + where + F: FnOnce(&mut [u8]) -> Result, + { + unsafe { + ffi::init(); + let mut cb = CallbackState::new(callback); + let bio = MemBioSlice::new(der)?; + cvt_p(ffi::d2i_PKCS8PrivateKey_bio( + bio.as_ptr(), + ptr::null_mut(), + Some(invoke_passwd_cb::), + &mut cb as *mut _ as *mut _, + )) + .map(|p| PKey::from_ptr(p)) + } + } + + /// Deserializes a DER-formatted PKCS#8 private key, using the supplied password if the key is + /// encrypted. + /// + /// # Panics + /// + /// Panics if `passphrase` contains an embedded null. + pub fn private_key_from_pkcs8_passphrase( + der: &[u8], + passphrase: &[u8], + ) -> Result, ErrorStack> { + unsafe { + ffi::init(); + let bio = MemBioSlice::new(der)?; + let passphrase = CString::new(passphrase).unwrap(); + cvt_p(ffi::d2i_PKCS8PrivateKey_bio( + bio.as_ptr(), + ptr::null_mut(), + None, + passphrase.as_ptr() as *const _ as *mut _, + )) + .map(|p| PKey::from_ptr(p)) + } + } +} + +impl PKey { + from_pem! { + /// Decodes a PEM-encoded SubjectPublicKeyInfo structure. + /// + /// The input should have a header of `-----BEGIN PUBLIC KEY-----`. + /// + /// This corresponds to [`PEM_read_bio_PUBKEY`]. + /// + /// [`PEM_read_bio_PUBKEY`]: https://www.openssl.org/docs/man1.0.2/crypto/PEM_read_bio_PUBKEY.html + public_key_from_pem, + PKey, + ffi::PEM_read_bio_PUBKEY + } + + from_der! { + /// Decodes a DER-encoded SubjectPublicKeyInfo structure. + /// + /// This corresponds to [`d2i_PUBKEY`]. + /// + /// [`d2i_PUBKEY`]: https://www.openssl.org/docs/man1.1.0/crypto/d2i_PUBKEY.html + public_key_from_der, + PKey, + ffi::d2i_PUBKEY, + ::libc::c_long + } +} + +use crate::ffi::EVP_PKEY_up_ref; + +#[cfg(test)] +mod tests { + use crate::ec::EcKey; + use crate::nid::Nid; + use crate::rsa::Rsa; + use crate::symm::Cipher; + + use super::*; + + #[test] + fn test_to_password() { + let rsa = Rsa::generate(2048).unwrap(); + let pkey = PKey::from_rsa(rsa).unwrap(); + let pem = pkey + .private_key_to_pem_pkcs8_passphrase(Cipher::aes_128_cbc(), b"foobar") + .unwrap(); + PKey::private_key_from_pem_passphrase(&pem, b"foobar").unwrap(); + assert!(PKey::private_key_from_pem_passphrase(&pem, b"fizzbuzz").is_err()); + } + + #[test] + fn test_unencrypted_pkcs8() { + let key = include_bytes!("../test/pkcs8-nocrypt.der"); + PKey::private_key_from_pkcs8(key).unwrap(); + } + + #[test] + fn test_encrypted_pkcs8_passphrase() { + let key = include_bytes!("../test/pkcs8.der"); + PKey::private_key_from_pkcs8_passphrase(key, b"mypass").unwrap(); + } + + #[test] + fn test_encrypted_pkcs8_callback() { + let mut password_queried = false; + let key = include_bytes!("../test/pkcs8.der"); + PKey::private_key_from_pkcs8_callback(key, |password| { + password_queried = true; + password[..6].copy_from_slice(b"mypass"); + Ok(6) + }) + .unwrap(); + assert!(password_queried); + } + + #[test] + fn test_private_key_from_pem() { + let key = include_bytes!("../test/key.pem"); + PKey::private_key_from_pem(key).unwrap(); + } + + #[test] + fn test_public_key_from_pem() { + let key = include_bytes!("../test/key.pem.pub"); + PKey::public_key_from_pem(key).unwrap(); + } + + #[test] + fn test_public_key_from_der() { + let key = include_bytes!("../test/key.der.pub"); + PKey::public_key_from_der(key).unwrap(); + } + + #[test] + fn test_private_key_from_der() { + let key = include_bytes!("../test/key.der"); + PKey::private_key_from_der(key).unwrap(); + } + + #[test] + fn test_pem() { + let key = include_bytes!("../test/key.pem"); + let key = PKey::private_key_from_pem(key).unwrap(); + + let priv_key = key.private_key_to_pem_pkcs8().unwrap(); + let pub_key = key.public_key_to_pem().unwrap(); + + // As a super-simple verification, just check that the buffers contain + // the `PRIVATE KEY` or `PUBLIC KEY` strings. + assert!(priv_key.windows(11).any(|s| s == b"PRIVATE KEY")); + assert!(pub_key.windows(10).any(|s| s == b"PUBLIC KEY")); + } + + #[test] + fn test_der_pkcs8() { + let key = include_bytes!("../test/key.der"); + let key = PKey::private_key_from_der(key).unwrap(); + + let priv_key = key.private_key_to_der_pkcs8().unwrap(); + + // Check that this has the correct PKCS#8 version number and algorithm. + assert_eq!(hex::encode(&priv_key[4..=6]), "020100"); // Version 0 + assert_eq!(hex::encode(&priv_key[9..=19]), "06092a864886f70d010101"); // Algorithm RSA/PKCS#1 + } + + #[test] + fn test_rsa_accessor() { + let rsa = Rsa::generate(2048).unwrap(); + let pkey = PKey::from_rsa(rsa).unwrap(); + pkey.rsa().unwrap(); + assert_eq!(pkey.id(), Id::RSA); + assert!(pkey.dsa().is_err()); + } + + #[test] + fn test_ec_key_accessor() { + let ec_key = EcKey::from_curve_name(Nid::X9_62_PRIME256V1).unwrap(); + let pkey = PKey::from_ec_key(ec_key).unwrap(); + pkey.ec_key().unwrap(); + assert_eq!(pkey.id(), Id::EC); + assert!(pkey.rsa().is_err()); + } +} diff --git a/crates/boring/src/rand.rs b/crates/boring/src/rand.rs new file mode 100644 index 000000000..c551b2f9e --- /dev/null +++ b/crates/boring/src/rand.rs @@ -0,0 +1,52 @@ +//! Utilities for secure random number generation. +//! +//! # Examples +//! +//! To generate a buffer with cryptographically strong bytes: +//! +//! ``` +//! use boring::rand::rand_bytes; +//! +//! let mut buf = [0; 256]; +//! rand_bytes(&mut buf).unwrap(); +//! ``` +use crate::ffi; +use libc::c_int; + +use crate::cvt; +use crate::error::ErrorStack; + +/// Fill buffer with cryptographically strong pseudo-random bytes. +/// +/// This corresponds to [`RAND_bytes`]. +/// +/// # Examples +/// +/// To generate a buffer with cryptographically strong bytes: +/// +/// ``` +/// use boring::rand::rand_bytes; +/// +/// let mut buf = [0; 256]; +/// rand_bytes(&mut buf).unwrap(); +/// ``` +/// +/// [`RAND_bytes`]: https://www.openssl.org/docs/man1.1.0/crypto/RAND_bytes.html +pub fn rand_bytes(buf: &mut [u8]) -> Result<(), ErrorStack> { + unsafe { + ffi::init(); + assert!(buf.len() <= c_int::max_value() as usize); + cvt(ffi::RAND_bytes(buf.as_mut_ptr(), buf.len())).map(|_| ()) + } +} + +#[cfg(test)] +mod tests { + use super::rand_bytes; + + #[test] + fn test_rand_bytes() { + let mut buf = [0; 32]; + rand_bytes(&mut buf).unwrap(); + } +} diff --git a/crates/boring/src/rsa.rs b/crates/boring/src/rsa.rs new file mode 100644 index 000000000..c17b36a3b --- /dev/null +++ b/crates/boring/src/rsa.rs @@ -0,0 +1,872 @@ +//! Rivest–Shamir–Adleman cryptosystem +//! +//! RSA is one of the earliest asymmetric public key encryption schemes. +//! Like many other cryptosystems, RSA relies on the presumed difficulty of a hard +//! mathematical problem, namely factorization of the product of two large prime +//! numbers. At the moment there does not exist an algorithm that can factor such +//! large numbers in reasonable time. RSA is used in a wide variety of +//! applications including digital signatures and key exchanges such as +//! establishing a TLS/SSL connection. +//! +//! The RSA acronym is derived from the first letters of the surnames of the +//! algorithm's founding trio. +//! +//! # Example +//! +//! Generate a 2048-bit RSA key pair and use the public key to encrypt some data. +//! +//! ```rust +//! use boring::rsa::{Rsa, Padding}; +//! +//! let rsa = Rsa::generate(2048).unwrap(); +//! let data = b"foobar"; +//! let mut buf = vec![0; rsa.size() as usize]; +//! let encrypted_len = rsa.public_encrypt(data, &mut buf, Padding::PKCS1).unwrap(); +//! ``` +use crate::ffi; +use foreign_types::{ForeignType, ForeignTypeRef}; +use libc::c_int; +use std::fmt; +use std::mem; +use std::ptr; + +use crate::bn::{BigNum, BigNumRef}; +use crate::error::ErrorStack; +use crate::pkey::{HasPrivate, HasPublic, Private, Public}; +use crate::{cvt, cvt_n, cvt_p}; + +pub const EVP_PKEY_OP_SIGN: c_int = 1 << 3; +pub const EVP_PKEY_OP_VERIFY: c_int = 1 << 4; +pub const EVP_PKEY_OP_VERIFYRECOVER: c_int = 1 << 5; +pub const EVP_PKEY_OP_SIGNCTX: c_int = 1 << 6; +pub const EVP_PKEY_OP_VERIFYCTX: c_int = 1 << 7; +pub const EVP_PKEY_OP_ENCRYPT: c_int = 1 << 8; +pub const EVP_PKEY_OP_DECRYPT: c_int = 1 << 9; + +pub const EVP_PKEY_OP_TYPE_SIG: c_int = EVP_PKEY_OP_SIGN + | EVP_PKEY_OP_VERIFY + | EVP_PKEY_OP_VERIFYRECOVER + | EVP_PKEY_OP_SIGNCTX + | EVP_PKEY_OP_VERIFYCTX; + +pub const EVP_PKEY_OP_TYPE_CRYPT: c_int = EVP_PKEY_OP_ENCRYPT | EVP_PKEY_OP_DECRYPT; + +/// Type of encryption padding to use. +/// +/// Random length padding is primarily used to prevent attackers from +/// predicting or knowing the exact length of a plaintext message that +/// can possibly lead to breaking encryption. +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +pub struct Padding(c_int); + +impl Padding { + pub const NONE: Padding = Padding(ffi::RSA_NO_PADDING); + pub const PKCS1: Padding = Padding(ffi::RSA_PKCS1_PADDING); + pub const PKCS1_OAEP: Padding = Padding(ffi::RSA_PKCS1_OAEP_PADDING); + pub const PKCS1_PSS: Padding = Padding(ffi::RSA_PKCS1_PSS_PADDING); + + /// Creates a `Padding` from an integer representation. + pub fn from_raw(value: c_int) -> Padding { + Padding(value) + } + + /// Returns the integer representation of `Padding`. + #[allow(clippy::trivially_copy_pass_by_ref)] + pub fn as_raw(&self) -> c_int { + self.0 + } +} + +generic_foreign_type_and_impl_send_sync! { + type CType = ffi::RSA; + fn drop = ffi::RSA_free; + + /// An RSA key. + pub struct Rsa; + + /// Reference to `RSA` + pub struct RsaRef; +} + +impl Clone for Rsa { + fn clone(&self) -> Rsa { + (**self).to_owned() + } +} + +impl ToOwned for RsaRef { + type Owned = Rsa; + + fn to_owned(&self) -> Rsa { + unsafe { + ffi::RSA_up_ref(self.as_ptr()); + Rsa::from_ptr(self.as_ptr()) + } + } +} + +impl RsaRef +where + T: HasPrivate, +{ + private_key_to_pem! { + /// Serializes the private key to a PEM-encoded PKCS#1 RSAPrivateKey structure. + /// + /// The output will have a header of `-----BEGIN RSA PRIVATE KEY-----`. + /// + /// This corresponds to [`PEM_write_bio_RSAPrivateKey`]. + /// + /// [`PEM_write_bio_RSAPrivateKey`]: https://www.openssl.org/docs/man1.1.0/crypto/PEM_write_bio_RSAPrivateKey.html + private_key_to_pem, + /// Serializes the private key to a PEM-encoded encrypted PKCS#1 RSAPrivateKey structure. + /// + /// The output will have a header of `-----BEGIN RSA PRIVATE KEY-----`. + /// + /// This corresponds to [`PEM_write_bio_RSAPrivateKey`]. + /// + /// [`PEM_write_bio_RSAPrivateKey`]: https://www.openssl.org/docs/man1.1.0/crypto/PEM_write_bio_RSAPrivateKey.html + private_key_to_pem_passphrase, + ffi::PEM_write_bio_RSAPrivateKey + } + + to_der! { + /// Serializes the private key to a DER-encoded PKCS#1 RSAPrivateKey structure. + /// + /// This corresponds to [`i2d_RSAPrivateKey`]. + /// + /// [`i2d_RSAPrivateKey`]: https://www.openssl.org/docs/man1.0.2/crypto/i2d_RSAPrivateKey.html + private_key_to_der, + ffi::i2d_RSAPrivateKey + } + + /// Decrypts data using the private key, returning the number of decrypted bytes. + /// + /// # Panics + /// + /// Panics if `self` has no private components, or if `to` is smaller + /// than `self.size()`. + pub fn private_decrypt( + &self, + from: &[u8], + to: &mut [u8], + padding: Padding, + ) -> Result { + assert!(from.len() <= i32::max_value() as usize); + assert!(to.len() >= self.size() as usize); + + unsafe { + let len = cvt_n(ffi::RSA_private_decrypt( + from.len(), + from.as_ptr(), + to.as_mut_ptr(), + self.as_ptr(), + padding.0, + ))?; + Ok(len as usize) + } + } + + /// Encrypts data using the private key, returning the number of encrypted bytes. + /// + /// # Panics + /// + /// Panics if `self` has no private components, or if `to` is smaller + /// than `self.size()`. + pub fn private_encrypt( + &self, + from: &[u8], + to: &mut [u8], + padding: Padding, + ) -> Result { + assert!(from.len() <= i32::max_value() as usize); + assert!(to.len() >= self.size() as usize); + + unsafe { + let len = cvt_n(ffi::RSA_private_encrypt( + from.len(), + from.as_ptr(), + to.as_mut_ptr(), + self.as_ptr(), + padding.0, + ))?; + Ok(len as usize) + } + } + + /// Returns a reference to the private exponent of the key. + /// + /// This corresponds to [`RSA_get0_key`]. + /// + /// [`RSA_get0_key`]: https://www.openssl.org/docs/man1.1.0/crypto/RSA_get0_key.html + pub fn d(&self) -> &BigNumRef { + unsafe { + let mut d = ptr::null(); + RSA_get0_key(self.as_ptr(), ptr::null_mut(), ptr::null_mut(), &mut d); + BigNumRef::from_ptr(d as *mut _) + } + } + + /// Returns a reference to the first factor of the exponent of the key. + /// + /// This corresponds to [`RSA_get0_factors`]. + /// + /// [`RSA_get0_factors`]: https://www.openssl.org/docs/man1.1.0/crypto/RSA_get0_key.html + pub fn p(&self) -> Option<&BigNumRef> { + unsafe { + let mut p = ptr::null(); + RSA_get0_factors(self.as_ptr(), &mut p, ptr::null_mut()); + if p.is_null() { + None + } else { + Some(BigNumRef::from_ptr(p as *mut _)) + } + } + } + + /// Returns a reference to the second factor of the exponent of the key. + /// + /// This corresponds to [`RSA_get0_factors`]. + /// + /// [`RSA_get0_factors`]: https://www.openssl.org/docs/man1.1.0/crypto/RSA_get0_key.html + pub fn q(&self) -> Option<&BigNumRef> { + unsafe { + let mut q = ptr::null(); + RSA_get0_factors(self.as_ptr(), ptr::null_mut(), &mut q); + if q.is_null() { + None + } else { + Some(BigNumRef::from_ptr(q as *mut _)) + } + } + } + + /// Returns a reference to the first exponent used for CRT calculations. + /// + /// This corresponds to [`RSA_get0_crt_params`]. + /// + /// [`RSA_get0_crt_params`]: https://www.openssl.org/docs/man1.1.0/crypto/RSA_get0_key.html + pub fn dmp1(&self) -> Option<&BigNumRef> { + unsafe { + let mut dp = ptr::null(); + RSA_get0_crt_params(self.as_ptr(), &mut dp, ptr::null_mut(), ptr::null_mut()); + if dp.is_null() { + None + } else { + Some(BigNumRef::from_ptr(dp as *mut _)) + } + } + } + + /// Returns a reference to the second exponent used for CRT calculations. + /// + /// This corresponds to [`RSA_get0_crt_params`]. + /// + /// [`RSA_get0_crt_params`]: https://www.openssl.org/docs/man1.1.0/crypto/RSA_get0_key.html + pub fn dmq1(&self) -> Option<&BigNumRef> { + unsafe { + let mut dq = ptr::null(); + RSA_get0_crt_params(self.as_ptr(), ptr::null_mut(), &mut dq, ptr::null_mut()); + if dq.is_null() { + None + } else { + Some(BigNumRef::from_ptr(dq as *mut _)) + } + } + } + + /// Returns a reference to the coefficient used for CRT calculations. + /// + /// This corresponds to [`RSA_get0_crt_params`]. + /// + /// [`RSA_get0_crt_params`]: https://www.openssl.org/docs/man1.1.0/crypto/RSA_get0_key.html + pub fn iqmp(&self) -> Option<&BigNumRef> { + unsafe { + let mut qi = ptr::null(); + RSA_get0_crt_params(self.as_ptr(), ptr::null_mut(), ptr::null_mut(), &mut qi); + if qi.is_null() { + None + } else { + Some(BigNumRef::from_ptr(qi as *mut _)) + } + } + } + + /// Validates RSA parameters for correctness + /// + /// This corresponds to [`RSA_check_key`]. + /// + /// [`RSA_check_key`]: https://www.openssl.org/docs/man1.1.0/crypto/RSA_check_key.html + #[allow(clippy::unnecessary_cast)] + pub fn check_key(&self) -> Result { + unsafe { + let result = ffi::RSA_check_key(self.as_ptr()) as i32; + if result == -1 { + Err(ErrorStack::get()) + } else { + Ok(result == 1) + } + } + } +} + +impl RsaRef +where + T: HasPublic, +{ + to_pem! { + /// Serializes the public key into a PEM-encoded SubjectPublicKeyInfo structure. + /// + /// The output will have a header of `-----BEGIN PUBLIC KEY-----`. + /// + /// This corresponds to [`PEM_write_bio_RSA_PUBKEY`]. + /// + /// [`PEM_write_bio_RSA_PUBKEY`]: https://www.openssl.org/docs/man1.0.2/crypto/pem.html + public_key_to_pem, + ffi::PEM_write_bio_RSA_PUBKEY + } + + to_der! { + /// Serializes the public key into a DER-encoded SubjectPublicKeyInfo structure. + /// + /// This corresponds to [`i2d_RSA_PUBKEY`]. + /// + /// [`i2d_RSA_PUBKEY`]: https://www.openssl.org/docs/man1.1.0/crypto/i2d_RSA_PUBKEY.html + public_key_to_der, + ffi::i2d_RSA_PUBKEY + } + + to_pem! { + /// Serializes the public key into a PEM-encoded PKCS#1 RSAPublicKey structure. + /// + /// The output will have a header of `-----BEGIN RSA PUBLIC KEY-----`. + /// + /// This corresponds to [`PEM_write_bio_RSAPublicKey`]. + /// + /// [`PEM_write_bio_RSAPublicKey`]: https://www.openssl.org/docs/man1.0.2/crypto/pem.html + public_key_to_pem_pkcs1, + ffi::PEM_write_bio_RSAPublicKey + } + + to_der! { + /// Serializes the public key into a DER-encoded PKCS#1 RSAPublicKey structure. + /// + /// This corresponds to [`i2d_RSAPublicKey`]. + /// + /// [`i2d_RSAPublicKey`]: https://www.openssl.org/docs/man1.0.2/crypto/i2d_RSAPublicKey.html + public_key_to_der_pkcs1, + ffi::i2d_RSAPublicKey + } + + /// Returns the size of the modulus in bytes. + /// + /// This corresponds to [`RSA_size`]. + /// + /// [`RSA_size`]: https://www.openssl.org/docs/man1.1.0/crypto/RSA_size.html + #[allow(clippy::unnecessary_cast)] + pub fn size(&self) -> u32 { + unsafe { ffi::RSA_size(self.as_ptr()) as u32 } + } + + /// Decrypts data using the public key, returning the number of decrypted bytes. + /// + /// # Panics + /// + /// Panics if `to` is smaller than `self.size()`. + pub fn public_decrypt( + &self, + from: &[u8], + to: &mut [u8], + padding: Padding, + ) -> Result { + assert!(from.len() <= i32::max_value() as usize); + assert!(to.len() >= self.size() as usize); + + unsafe { + let len = cvt_n(ffi::RSA_public_decrypt( + from.len(), + from.as_ptr(), + to.as_mut_ptr(), + self.as_ptr(), + padding.0, + ))?; + Ok(len as usize) + } + } + + /// Encrypts data using the public key, returning the number of encrypted bytes. + /// + /// # Panics + /// + /// Panics if `to` is smaller than `self.size()`. + pub fn public_encrypt( + &self, + from: &[u8], + to: &mut [u8], + padding: Padding, + ) -> Result { + assert!(from.len() <= i32::max_value() as usize); + assert!(to.len() >= self.size() as usize); + + unsafe { + let len = cvt_n(ffi::RSA_public_encrypt( + from.len(), + from.as_ptr(), + to.as_mut_ptr(), + self.as_ptr(), + padding.0, + ))?; + Ok(len as usize) + } + } + + /// Returns a reference to the modulus of the key. + /// + /// This corresponds to [`RSA_get0_key`]. + /// + /// [`RSA_get0_key`]: https://www.openssl.org/docs/man1.1.0/crypto/RSA_get0_key.html + pub fn n(&self) -> &BigNumRef { + unsafe { + let mut n = ptr::null(); + RSA_get0_key(self.as_ptr(), &mut n, ptr::null_mut(), ptr::null_mut()); + BigNumRef::from_ptr(n as *mut _) + } + } + + /// Returns a reference to the public exponent of the key. + /// + /// This corresponds to [`RSA_get0_key`]. + /// + /// [`RSA_get0_key`]: https://www.openssl.org/docs/man1.1.0/crypto/RSA_get0_key.html + pub fn e(&self) -> &BigNumRef { + unsafe { + let mut e = ptr::null(); + RSA_get0_key(self.as_ptr(), ptr::null_mut(), &mut e, ptr::null_mut()); + BigNumRef::from_ptr(e as *mut _) + } + } +} + +impl Rsa { + /// Creates a new RSA key with only public components. + /// + /// `n` is the modulus common to both public and private key. + /// `e` is the public exponent. + /// + /// This corresponds to [`RSA_new`] and uses [`RSA_set0_key`]. + /// + /// [`RSA_new`]: https://www.openssl.org/docs/man1.1.0/crypto/RSA_new.html + /// [`RSA_set0_key`]: https://www.openssl.org/docs/man1.1.0/crypto/RSA_set0_key.html + pub fn from_public_components(n: BigNum, e: BigNum) -> Result, ErrorStack> { + unsafe { + let rsa = cvt_p(ffi::RSA_new())?; + RSA_set0_key(rsa, n.as_ptr(), e.as_ptr(), ptr::null_mut()); + mem::forget((n, e)); + Ok(Rsa::from_ptr(rsa)) + } + } + + from_pem! { + /// Decodes a PEM-encoded SubjectPublicKeyInfo structure containing an RSA key. + /// + /// The input should have a header of `-----BEGIN PUBLIC KEY-----`. + /// + /// This corresponds to [`PEM_read_bio_RSA_PUBKEY`]. + /// + /// [`PEM_read_bio_RSA_PUBKEY`]: https://www.openssl.org/docs/man1.0.2/crypto/PEM_read_bio_RSA_PUBKEY.html + public_key_from_pem, + Rsa, + ffi::PEM_read_bio_RSA_PUBKEY + } + + from_pem! { + /// Decodes a PEM-encoded PKCS#1 RSAPublicKey structure. + /// + /// The input should have a header of `-----BEGIN RSA PUBLIC KEY-----`. + /// + /// This corresponds to [`PEM_read_bio_RSAPublicKey`]. + /// + /// [`PEM_read_bio_RSAPublicKey`]: https://www.openssl.org/docs/man1.0.2/crypto/PEM_read_bio_RSAPublicKey.html + public_key_from_pem_pkcs1, + Rsa, + ffi::PEM_read_bio_RSAPublicKey + } + + from_der! { + /// Decodes a DER-encoded SubjectPublicKeyInfo structure containing an RSA key. + /// + /// This corresponds to [`d2i_RSA_PUBKEY`]. + /// + /// [`d2i_RSA_PUBKEY`]: https://www.openssl.org/docs/man1.0.2/crypto/d2i_RSA_PUBKEY.html + public_key_from_der, + Rsa, + ffi::d2i_RSA_PUBKEY, + ::libc::c_long + } + + from_der! { + /// Decodes a DER-encoded PKCS#1 RSAPublicKey structure. + /// + /// This corresponds to [`d2i_RSAPublicKey`]. + /// + /// [`d2i_RSAPublicKey`]: https://www.openssl.org/docs/man1.0.2/crypto/d2i_RSA_PUBKEY.html + public_key_from_der_pkcs1, + Rsa, + ffi::d2i_RSAPublicKey, + ::libc::c_long + } +} + +pub struct RsaPrivateKeyBuilder { + rsa: Rsa, +} + +impl RsaPrivateKeyBuilder { + /// Creates a new `RsaPrivateKeyBuilder`. + /// + /// `n` is the modulus common to both public and private key. + /// `e` is the public exponent and `d` is the private exponent. + /// + /// This corresponds to [`RSA_new`] and uses [`RSA_set0_key`]. + /// + /// [`RSA_new`]: https://www.openssl.org/docs/man1.1.0/crypto/RSA_new.html + /// [`RSA_set0_key`]: https://www.openssl.org/docs/man1.1.0/crypto/RSA_set0_key.html + pub fn new(n: BigNum, e: BigNum, d: BigNum) -> Result { + unsafe { + let rsa = cvt_p(ffi::RSA_new())?; + RSA_set0_key(rsa, n.as_ptr(), e.as_ptr(), d.as_ptr()); + mem::forget((n, e, d)); + Ok(RsaPrivateKeyBuilder { + rsa: Rsa::from_ptr(rsa), + }) + } + } + + /// Sets the factors of the Rsa key. + /// + /// `p` and `q` are the first and second factors of `n`. + /// + /// This correspond to [`RSA_set0_factors`]. + /// + /// [`RSA_set0_factors`]: https://www.openssl.org/docs/man1.1.0/crypto/RSA_set0_factors.html + // FIXME should be infallible + pub fn set_factors(self, p: BigNum, q: BigNum) -> Result { + unsafe { + RSA_set0_factors(self.rsa.as_ptr(), p.as_ptr(), q.as_ptr()); + mem::forget((p, q)); + } + Ok(self) + } + + /// Sets the Chinese Remainder Theorem params of the Rsa key. + /// + /// `dmp1`, `dmq1`, and `iqmp` are the exponents and coefficient for + /// CRT calculations which is used to speed up RSA operations. + /// + /// This correspond to [`RSA_set0_crt_params`]. + /// + /// [`RSA_set0_crt_params`]: https://www.openssl.org/docs/man1.1.0/crypto/RSA_set0_crt_params.html + // FIXME should be infallible + pub fn set_crt_params( + self, + dmp1: BigNum, + dmq1: BigNum, + iqmp: BigNum, + ) -> Result { + unsafe { + RSA_set0_crt_params( + self.rsa.as_ptr(), + dmp1.as_ptr(), + dmq1.as_ptr(), + iqmp.as_ptr(), + ); + mem::forget((dmp1, dmq1, iqmp)); + } + Ok(self) + } + + /// Returns the Rsa key. + pub fn build(self) -> Rsa { + self.rsa + } +} + +impl Rsa { + /// Creates a new RSA key with private components (public components are assumed). + /// + /// This a convenience method over + /// `Rsa::build(n, e, d)?.set_factors(p, q)?.set_crt_params(dmp1, dmq1, iqmp)?.build()` + #[allow(clippy::too_many_arguments, clippy::many_single_char_names)] + pub fn from_private_components( + n: BigNum, + e: BigNum, + d: BigNum, + p: BigNum, + q: BigNum, + dmp1: BigNum, + dmq1: BigNum, + iqmp: BigNum, + ) -> Result, ErrorStack> { + Ok(RsaPrivateKeyBuilder::new(n, e, d)? + .set_factors(p, q)? + .set_crt_params(dmp1, dmq1, iqmp)? + .build()) + } + + /// Generates a public/private key pair with the specified size. + /// + /// The public exponent will be 65537. + /// + /// This corresponds to [`RSA_generate_key_ex`]. + /// + /// [`RSA_generate_key_ex`]: https://www.openssl.org/docs/man1.1.0/crypto/RSA_generate_key_ex.html + pub fn generate(bits: u32) -> Result, ErrorStack> { + let e = BigNum::from_u32(ffi::RSA_F4 as u32)?; + Rsa::generate_with_e(bits, &e) + } + + /// Generates a public/private key pair with the specified size and a custom exponent. + /// + /// Unless you have specific needs and know what you're doing, use `Rsa::generate` instead. + /// + /// This corresponds to [`RSA_generate_key_ex`]. + /// + /// [`RSA_generate_key_ex`]: https://www.openssl.org/docs/man1.1.0/crypto/RSA_generate_key_ex.html + pub fn generate_with_e(bits: u32, e: &BigNumRef) -> Result, ErrorStack> { + unsafe { + let rsa = Rsa::from_ptr(cvt_p(ffi::RSA_new())?); + cvt(ffi::RSA_generate_key_ex( + rsa.0, + bits as c_int, + e.as_ptr(), + ptr::null_mut(), + ))?; + Ok(rsa) + } + } + + // FIXME these need to identify input formats + private_key_from_pem! { + /// Deserializes a private key from a PEM-encoded PKCS#1 RSAPrivateKey structure. + /// + /// This corresponds to [`PEM_read_bio_RSAPrivateKey`]. + /// + /// [`PEM_read_bio_RSAPrivateKey`]: https://www.openssl.org/docs/man1.1.0/crypto/PEM_read_bio_RSAPrivateKey.html + private_key_from_pem, + + /// Deserializes a private key from a PEM-encoded encrypted PKCS#1 RSAPrivateKey structure. + /// + /// This corresponds to [`PEM_read_bio_RSAPrivateKey`]. + /// + /// [`PEM_read_bio_RSAPrivateKey`]: https://www.openssl.org/docs/man1.1.0/crypto/PEM_read_bio_RSAPrivateKey.html + private_key_from_pem_passphrase, + + /// Deserializes a private key from a PEM-encoded encrypted PKCS#1 RSAPrivateKey structure. + /// + /// The callback should fill the password into the provided buffer and return its length. + /// + /// This corresponds to [`PEM_read_bio_RSAPrivateKey`]. + /// + /// [`PEM_read_bio_RSAPrivateKey`]: https://www.openssl.org/docs/man1.1.0/crypto/PEM_read_bio_RSAPrivateKey.html + private_key_from_pem_callback, + Rsa, + ffi::PEM_read_bio_RSAPrivateKey + } + + from_der! { + /// Decodes a DER-encoded PKCS#1 RSAPrivateKey structure. + /// + /// This corresponds to [`d2i_RSAPrivateKey`]. + /// + /// [`d2i_RSAPrivateKey`]: https://www.openssl.org/docs/man1.0.2/crypto/d2i_RSA_PUBKEY.html + private_key_from_der, + Rsa, + ffi::d2i_RSAPrivateKey, + ::libc::c_long + } +} + +impl fmt::Debug for Rsa { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "Rsa") + } +} + +use crate::ffi::{ + RSA_get0_crt_params, RSA_get0_factors, RSA_get0_key, RSA_set0_crt_params, RSA_set0_factors, + RSA_set0_key, +}; + +#[cfg(test)] +mod test { + use crate::symm::Cipher; + + use super::*; + + #[test] + fn test_from_password() { + let key = include_bytes!("../test/rsa-encrypted.pem"); + Rsa::private_key_from_pem_passphrase(key, b"mypass").unwrap(); + } + + #[test] + fn test_from_password_callback() { + let mut password_queried = false; + let key = include_bytes!("../test/rsa-encrypted.pem"); + Rsa::private_key_from_pem_callback(key, |password| { + password_queried = true; + password[..6].copy_from_slice(b"mypass"); + Ok(6) + }) + .unwrap(); + + assert!(password_queried); + } + + #[test] + fn test_to_password() { + let key = Rsa::generate(2048).unwrap(); + let pem = key + .private_key_to_pem_passphrase(Cipher::aes_128_cbc(), b"foobar") + .unwrap(); + Rsa::private_key_from_pem_passphrase(&pem, b"foobar").unwrap(); + assert!(Rsa::private_key_from_pem_passphrase(&pem, b"fizzbuzz").is_err()); + } + + #[test] + fn test_public_encrypt_private_decrypt_with_padding() { + let key = include_bytes!("../test/rsa.pem.pub"); + let public_key = Rsa::public_key_from_pem(key).unwrap(); + + let mut result = vec![0; public_key.size() as usize]; + let original_data = b"This is test"; + let len = public_key + .public_encrypt(original_data, &mut result, Padding::PKCS1) + .unwrap(); + assert_eq!(len, 256); + + let pkey = include_bytes!("../test/rsa.pem"); + let private_key = Rsa::private_key_from_pem(pkey).unwrap(); + let mut dec_result = vec![0; private_key.size() as usize]; + let len = private_key + .private_decrypt(&result, &mut dec_result, Padding::PKCS1) + .unwrap(); + + assert_eq!(&dec_result[..len], original_data); + } + + #[test] + fn test_private_encrypt() { + let k0 = super::Rsa::generate(512).unwrap(); + let k0pkey = k0.public_key_to_pem().unwrap(); + let k1 = super::Rsa::public_key_from_pem(&k0pkey).unwrap(); + + let msg = vec![0xdeu8, 0xadu8, 0xd0u8, 0x0du8]; + + let mut emesg = vec![0; k0.size() as usize]; + k0.private_encrypt(&msg, &mut emesg, Padding::PKCS1) + .unwrap(); + let mut dmesg = vec![0; k1.size() as usize]; + let len = k1 + .public_decrypt(&emesg, &mut dmesg, Padding::PKCS1) + .unwrap(); + assert_eq!(msg, &dmesg[..len]); + } + + #[test] + fn test_public_encrypt() { + let k0 = super::Rsa::generate(512).unwrap(); + let k0pkey = k0.private_key_to_pem().unwrap(); + let k1 = super::Rsa::private_key_from_pem(&k0pkey).unwrap(); + + let msg = vec![0xdeu8, 0xadu8, 0xd0u8, 0x0du8]; + + let mut emesg = vec![0; k0.size() as usize]; + k0.public_encrypt(&msg, &mut emesg, Padding::PKCS1).unwrap(); + let mut dmesg = vec![0; k1.size() as usize]; + let len = k1 + .private_decrypt(&emesg, &mut dmesg, Padding::PKCS1) + .unwrap(); + assert_eq!(msg, &dmesg[..len]); + } + + #[test] + fn test_public_key_from_pem_pkcs1() { + let key = include_bytes!("../test/pkcs1.pem.pub"); + Rsa::public_key_from_pem_pkcs1(key).unwrap(); + } + + #[test] + #[should_panic] + fn test_public_key_from_pem_pkcs1_file_panic() { + let key = include_bytes!("../test/key.pem.pub"); + Rsa::public_key_from_pem_pkcs1(key).unwrap(); + } + + #[test] + fn test_public_key_to_pem_pkcs1() { + let keypair = super::Rsa::generate(512).unwrap(); + let pubkey_pem = keypair.public_key_to_pem_pkcs1().unwrap(); + super::Rsa::public_key_from_pem_pkcs1(&pubkey_pem).unwrap(); + } + + #[test] + #[should_panic] + fn test_public_key_from_pem_pkcs1_generate_panic() { + let keypair = super::Rsa::generate(512).unwrap(); + let pubkey_pem = keypair.public_key_to_pem().unwrap(); + super::Rsa::public_key_from_pem_pkcs1(&pubkey_pem).unwrap(); + } + + #[test] + fn test_pem_pkcs1_encrypt() { + let keypair = super::Rsa::generate(2048).unwrap(); + let pubkey_pem = keypair.public_key_to_pem_pkcs1().unwrap(); + let pubkey = super::Rsa::public_key_from_pem_pkcs1(&pubkey_pem).unwrap(); + let msg = b"Hello, world!"; + + let mut encrypted = vec![0; pubkey.size() as usize]; + let len = pubkey + .public_encrypt(msg, &mut encrypted, Padding::PKCS1) + .unwrap(); + assert!(len > msg.len()); + let mut decrypted = vec![0; keypair.size() as usize]; + let len = keypair + .private_decrypt(&encrypted, &mut decrypted, Padding::PKCS1) + .unwrap(); + assert_eq!(len, msg.len()); + assert_eq!(&decrypted[..len], msg); + } + + #[test] + fn test_pem_pkcs1_padding() { + let keypair = super::Rsa::generate(2048).unwrap(); + let pubkey_pem = keypair.public_key_to_pem_pkcs1().unwrap(); + let pubkey = super::Rsa::public_key_from_pem_pkcs1(&pubkey_pem).unwrap(); + let msg = b"foo"; + + let mut encrypted1 = vec![0; pubkey.size() as usize]; + let mut encrypted2 = vec![0; pubkey.size() as usize]; + let len1 = pubkey + .public_encrypt(msg, &mut encrypted1, Padding::PKCS1) + .unwrap(); + let len2 = pubkey + .public_encrypt(msg, &mut encrypted2, Padding::PKCS1) + .unwrap(); + assert!(len1 > (msg.len() + 1)); + assert_eq!(len1, len2); + assert_ne!(encrypted1, encrypted2); + } + + #[test] + #[allow(clippy::redundant_clone)] + fn clone() { + let key = Rsa::generate(2048).unwrap(); + drop(key.clone()); + } + + #[test] + fn generate_with_e() { + let e = BigNum::from_u32(0x10001).unwrap(); + Rsa::generate_with_e(2048, &e).unwrap(); + } +} diff --git a/crates/boring/src/sha.rs b/crates/boring/src/sha.rs new file mode 100644 index 000000000..98aa26ba9 --- /dev/null +++ b/crates/boring/src/sha.rs @@ -0,0 +1,531 @@ +//! The SHA family of hashes. +//! +//! SHA, or Secure Hash Algorithms, are a family of cryptographic hashing algorithms published by +//! the National Institute of Standards and Technology (NIST). Hash algorithms such as those in +//! the SHA family are used to map data of an arbitrary size to a fixed-size string of bytes. +//! As cryptographic hashing algorithms, these mappings have the property of being irreversable. +//! This property makes hash algorithms like these excellent for uses such as verifying the +//! contents of a file- if you know the hash you expect beforehand, then you can verify that the +//! data you have is correct if it hashes to the same value. +//! +//! # Examples +//! +//! When dealing with data that becomes available in chunks, such as while buffering data from IO, +//! you can create a hasher that you can repeatedly update to add bytes to. +//! +//! ```rust +//! extern crate hex; +//! +//! use boring::sha; +//! +//! fn main() { +//! let mut hasher = sha::Sha256::new(); +//! +//! hasher.update(b"Hello, "); +//! hasher.update(b"world"); +//! +//! let hash = hasher.finish(); +//! println!("Hashed \"Hello, world\" to {}", hex::encode(hash)); +//! } +//! ``` +//! +//! On the other hand, if you already have access to all of the data you woud like to hash, you +//! may prefer to use the slightly simpler method of simply calling the hash function corresponding +//! to the algorithm you want to use. +//! +//! ```rust +//! extern crate hex; +//! +//! use boring::sha::sha256; +//! +//! fn main() { +//! let hash = sha256(b"your data or message"); +//! println!("Hash = {}", hex::encode(hash)); +//! } +//! ``` +use crate::ffi; +use libc::c_void; +use std::mem::MaybeUninit; + +/// Computes the SHA1 hash of some data. +/// +/// # Warning +/// +/// SHA1 is known to be insecure - it should not be used unless required for +/// compatibility with existing systems. +#[inline] +#[allow(deprecated)] // https://github.com/rust-lang/rust/issues/63566 +pub fn sha1(data: &[u8]) -> [u8; 20] { + unsafe { + let mut hash: MaybeUninit<[u8; 20]> = MaybeUninit::uninit(); + ffi::SHA1(data.as_ptr(), data.len(), hash.as_mut_ptr().cast()); + hash.assume_init() + } +} + +/// Computes the SHA224 hash of some data. +#[inline] +#[allow(deprecated)] // https://github.com/rust-lang/rust/issues/63566 +pub fn sha224(data: &[u8]) -> [u8; 28] { + unsafe { + let mut hash: MaybeUninit<[u8; 28]> = MaybeUninit::uninit(); + ffi::SHA224(data.as_ptr(), data.len(), hash.as_mut_ptr().cast()); + hash.assume_init() + } +} + +/// Computes the SHA256 hash of some data. +#[inline] +#[allow(deprecated)] // https://github.com/rust-lang/rust/issues/63566 +pub fn sha256(data: &[u8]) -> [u8; 32] { + unsafe { + let mut hash: MaybeUninit<[u8; 32]> = MaybeUninit::uninit(); + ffi::SHA256(data.as_ptr(), data.len(), hash.as_mut_ptr().cast()); + hash.assume_init() + } +} + +/// Computes the SHA384 hash of some data. +#[inline] +#[allow(deprecated)] // https://github.com/rust-lang/rust/issues/63566 +pub fn sha384(data: &[u8]) -> [u8; 48] { + unsafe { + let mut hash: MaybeUninit<[u8; 48]> = MaybeUninit::uninit(); + ffi::SHA384(data.as_ptr(), data.len(), hash.as_mut_ptr().cast()); + hash.assume_init() + } +} + +/// Computes the SHA512 hash of some data. +#[inline] +#[allow(deprecated)] // https://github.com/rust-lang/rust/issues/63566 +pub fn sha512(data: &[u8]) -> [u8; 64] { + unsafe { + let mut hash: MaybeUninit<[u8; 64]> = MaybeUninit::uninit(); + ffi::SHA512(data.as_ptr(), data.len(), hash.as_mut_ptr().cast()); + hash.assume_init() + } +} + +/// Computes the SHA512-256 hash of some data. +#[inline] +#[allow(deprecated)] // https://github.com/rust-lang/rust/issues/63566 +pub fn sha512_256(data: &[u8]) -> [u8; 32] { + unsafe { + let mut hash: MaybeUninit<[u8; 32]> = MaybeUninit::uninit(); + ffi::SHA512_256(data.as_ptr(), data.len(), hash.as_mut_ptr().cast()); + hash.assume_init() + } +} + +/// An object which calculates a SHA1 hash of some data. +/// +/// # Warning +/// +/// SHA1 is known to be insecure - it should not be used unless required for +/// compatibility with existing systems. +#[derive(Clone)] +pub struct Sha1(ffi::SHA_CTX); + +impl Default for Sha1 { + #[inline] + fn default() -> Sha1 { + Sha1::new() + } +} + +impl Sha1 { + /// Creates a new hasher. + #[inline] + #[allow(deprecated)] // https://github.com/rust-lang/rust/issues/63566 + pub fn new() -> Sha1 { + unsafe { + let mut ctx = MaybeUninit::uninit(); + ffi::SHA1_Init(ctx.as_mut_ptr()); + Sha1(ctx.assume_init()) + } + } + + /// Feeds some data into the hasher. + /// + /// This can be called multiple times. + #[inline] + pub fn update(&mut self, buf: &[u8]) { + unsafe { + ffi::SHA1_Update(&mut self.0, buf.as_ptr() as *const c_void, buf.len()); + } + } + + /// Returns the hash of the data. + #[inline] + #[allow(deprecated)] // https://github.com/rust-lang/rust/issues/63566 + pub fn finish(mut self) -> [u8; 20] { + unsafe { + let mut hash: MaybeUninit<[u8; 20]> = MaybeUninit::uninit(); + ffi::SHA1_Final(hash.as_mut_ptr().cast(), &mut self.0); + hash.assume_init() + } + } +} + +/// An object which calculates a SHA224 hash of some data. +#[derive(Clone)] +pub struct Sha224(ffi::SHA256_CTX); + +impl Default for Sha224 { + #[inline] + fn default() -> Sha224 { + Sha224::new() + } +} + +impl Sha224 { + /// Creates a new hasher. + #[inline] + #[allow(deprecated)] // https://github.com/rust-lang/rust/issues/63566 + pub fn new() -> Sha224 { + unsafe { + let mut ctx = MaybeUninit::uninit(); + ffi::SHA224_Init(ctx.as_mut_ptr()); + Sha224(ctx.assume_init()) + } + } + + /// Feeds some data into the hasher. + /// + /// This can be called multiple times. + #[inline] + pub fn update(&mut self, buf: &[u8]) { + unsafe { + ffi::SHA224_Update(&mut self.0, buf.as_ptr() as *const c_void, buf.len()); + } + } + + /// Returns the hash of the data. + #[inline] + #[allow(deprecated)] // https://github.com/rust-lang/rust/issues/63566 + pub fn finish(mut self) -> [u8; 28] { + unsafe { + let mut hash: MaybeUninit<[u8; 28]> = MaybeUninit::uninit(); + ffi::SHA224_Final(hash.as_mut_ptr().cast(), &mut self.0); + hash.assume_init() + } + } +} + +/// An object which calculates a SHA256 hash of some data. +#[derive(Clone)] +pub struct Sha256(ffi::SHA256_CTX); + +impl Default for Sha256 { + #[inline] + fn default() -> Sha256 { + Sha256::new() + } +} + +impl Sha256 { + /// Creates a new hasher. + #[inline] + #[allow(deprecated)] // https://github.com/rust-lang/rust/issues/63566 + pub fn new() -> Sha256 { + unsafe { + let mut ctx = MaybeUninit::uninit(); + ffi::SHA256_Init(ctx.as_mut_ptr()); + Sha256(ctx.assume_init()) + } + } + + /// Feeds some data into the hasher. + /// + /// This can be called multiple times. + #[inline] + pub fn update(&mut self, buf: &[u8]) { + unsafe { + ffi::SHA256_Update(&mut self.0, buf.as_ptr() as *const c_void, buf.len()); + } + } + + /// Returns the hash of the data. + #[inline] + #[allow(deprecated)] // https://github.com/rust-lang/rust/issues/63566 + pub fn finish(mut self) -> [u8; 32] { + unsafe { + let mut hash: MaybeUninit<[u8; 32]> = MaybeUninit::uninit(); + ffi::SHA256_Final(hash.as_mut_ptr().cast(), &mut self.0); + hash.assume_init() + } + } +} + +/// An object which calculates a SHA384 hash of some data. +#[derive(Clone)] +pub struct Sha384(ffi::SHA512_CTX); + +impl Default for Sha384 { + #[inline] + fn default() -> Sha384 { + Sha384::new() + } +} + +impl Sha384 { + /// Creates a new hasher. + #[inline] + #[allow(deprecated)] // https://github.com/rust-lang/rust/issues/63566 + pub fn new() -> Sha384 { + unsafe { + let mut ctx = MaybeUninit::uninit(); + ffi::SHA384_Init(ctx.as_mut_ptr()); + Sha384(ctx.assume_init()) + } + } + + /// Feeds some data into the hasher. + /// + /// This can be called multiple times. + #[inline] + pub fn update(&mut self, buf: &[u8]) { + unsafe { + ffi::SHA384_Update(&mut self.0, buf.as_ptr() as *const c_void, buf.len()); + } + } + + /// Returns the hash of the data. + #[inline] + #[allow(deprecated)] // https://github.com/rust-lang/rust/issues/63566 + pub fn finish(mut self) -> [u8; 48] { + unsafe { + let mut hash: MaybeUninit<[u8; 48]> = MaybeUninit::uninit(); + ffi::SHA384_Final(hash.as_mut_ptr().cast(), &mut self.0); + hash.assume_init() + } + } +} + +/// An object which calculates a SHA512 hash of some data. +#[derive(Clone)] +pub struct Sha512(ffi::SHA512_CTX); + +impl Default for Sha512 { + #[inline] + fn default() -> Sha512 { + Sha512::new() + } +} + +impl Sha512 { + /// Creates a new hasher. + #[inline] + #[allow(deprecated)] // https://github.com/rust-lang/rust/issues/63566 + pub fn new() -> Sha512 { + unsafe { + let mut ctx = MaybeUninit::uninit(); + ffi::SHA512_Init(ctx.as_mut_ptr()); + Sha512(ctx.assume_init()) + } + } + + /// Feeds some data into the hasher. + /// + /// This can be called multiple times. + #[inline] + pub fn update(&mut self, buf: &[u8]) { + unsafe { + ffi::SHA512_Update(&mut self.0, buf.as_ptr() as *const c_void, buf.len()); + } + } + + /// Returns the hash of the data. + #[inline] + #[allow(deprecated)] // https://github.com/rust-lang/rust/issues/63566 + pub fn finish(mut self) -> [u8; 64] { + unsafe { + let mut hash: MaybeUninit<[u8; 64]> = MaybeUninit::uninit(); + ffi::SHA512_Final(hash.as_mut_ptr().cast(), &mut self.0); + hash.assume_init() + } + } +} + +/// An object which calculates a SHA512-256 hash of some data. +#[derive(Clone)] +pub struct Sha512_256(ffi::SHA512_CTX); + +impl Default for Sha512_256 { + #[inline] + fn default() -> Sha512_256 { + Sha512_256::new() + } +} + +impl Sha512_256 { + /// Creates a new hasher. + #[inline] + #[allow(deprecated)] // https://github.com/rust-lang/rust/issues/63566 + pub fn new() -> Sha512_256 { + unsafe { + let mut ctx = MaybeUninit::uninit(); + ffi::SHA512_256_Init(ctx.as_mut_ptr()); + Sha512_256(ctx.assume_init()) + } + } + + /// Feeds some data into the hasher. + /// + /// This can be called multiple times. + #[inline] + pub fn update(&mut self, buf: &[u8]) { + unsafe { + ffi::SHA512_256_Update(&mut self.0, buf.as_ptr() as *const c_void, buf.len()); + } + } + + /// Returns the hash of the data. + #[inline] + #[allow(deprecated)] // https://github.com/rust-lang/rust/issues/63566 + pub fn finish(mut self) -> [u8; 32] { + unsafe { + let mut hash: MaybeUninit<[u8; 32]> = MaybeUninit::uninit(); + ffi::SHA512_256_Final(hash.as_mut_ptr().cast(), &mut self.0); + hash.assume_init() + } + } +} + +#[cfg(test)] +mod test { + use hex; + + use super::*; + + #[test] + fn standalone_1() { + let data = b"abc"; + let expected = "a9993e364706816aba3e25717850c26c9cd0d89d"; + + assert_eq!(hex::encode(sha1(data)), expected); + } + + #[test] + fn struct_1() { + let expected = "a9993e364706816aba3e25717850c26c9cd0d89d"; + + let mut hasher = Sha1::new(); + hasher.update(b"a"); + hasher.update(b"bc"); + assert_eq!(hex::encode(hasher.finish()), expected); + } + + #[test] + fn cloning_allows_incremental_hashing() { + let expected = "a9993e364706816aba3e25717850c26c9cd0d89d"; + + let mut hasher = Sha1::new(); + hasher.update(b"a"); + + let mut incr_hasher = hasher.clone(); + incr_hasher.update(b"bc"); + + assert_eq!(hex::encode(incr_hasher.finish()), expected); + assert_ne!(hex::encode(hasher.finish()), expected); + } + + #[test] + fn standalone_224() { + let data = b"abc"; + let expected = "23097d223405d8228642a477bda255b32aadbce4bda0b3f7e36c9da7"; + + assert_eq!(hex::encode(sha224(data)), expected); + } + + #[test] + fn struct_224() { + let expected = "23097d223405d8228642a477bda255b32aadbce4bda0b3f7e36c9da7"; + + let mut hasher = Sha224::new(); + hasher.update(b"a"); + hasher.update(b"bc"); + assert_eq!(hex::encode(hasher.finish()), expected); + } + + #[test] + fn standalone_256() { + let data = b"abc"; + let expected = "ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad"; + + assert_eq!(hex::encode(sha256(data)), expected); + } + + #[test] + fn struct_256() { + let expected = "ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad"; + + let mut hasher = Sha256::new(); + hasher.update(b"a"); + hasher.update(b"bc"); + assert_eq!(hex::encode(hasher.finish()), expected); + } + + #[test] + fn standalone_384() { + let data = b"abc"; + let expected = + "cb00753f45a35e8bb5a03d699ac65007272c32ab0eded1631a8b605a43ff5bed8086072ba1e\ + 7cc2358baeca134c825a7"; + + assert_eq!(hex::encode(&sha384(data)[..]), expected); + } + + #[test] + fn struct_384() { + let expected = + "cb00753f45a35e8bb5a03d699ac65007272c32ab0eded1631a8b605a43ff5bed8086072ba1e\ + 7cc2358baeca134c825a7"; + + let mut hasher = Sha384::new(); + hasher.update(b"a"); + hasher.update(b"bc"); + assert_eq!(hex::encode(&hasher.finish()[..]), expected); + } + + #[test] + fn standalone_512() { + let data = b"abc"; + let expected = + "ddaf35a193617abacc417349ae20413112e6fa4e89a97ea20a9eeee64b55d39a2192992a274\ + fc1a836ba3c23a3feebbd454d4423643ce80e2a9ac94fa54ca49f"; + + assert_eq!(hex::encode(&sha512(data)[..]), expected); + } + + #[test] + fn struct_512() { + let expected = + "ddaf35a193617abacc417349ae20413112e6fa4e89a97ea20a9eeee64b55d39a2192992a274\ + fc1a836ba3c23a3feebbd454d4423643ce80e2a9ac94fa54ca49f"; + + let mut hasher = Sha512::new(); + hasher.update(b"a"); + hasher.update(b"bc"); + assert_eq!(hex::encode(&hasher.finish()[..]), expected); + } + + #[test] + fn standalone_512_256() { + let data = b"abc"; + let expected = "53048e2681941ef99b2e29b76b4c7dabe4c2d0c634fc6d46e0e2f13107e7af23"; + + assert_eq!(hex::encode(&sha512_256(data)[..]), expected); + } + + #[test] + fn struct_512_256() { + let expected = "53048e2681941ef99b2e29b76b4c7dabe4c2d0c634fc6d46e0e2f13107e7af23"; + + let mut hasher = Sha512_256::new(); + hasher.update(b"a"); + hasher.update(b"bc"); + assert_eq!(hex::encode(&hasher.finish()[..]), expected); + } +} diff --git a/crates/boring/src/sign.rs b/crates/boring/src/sign.rs new file mode 100644 index 000000000..5bfa2bc44 --- /dev/null +++ b/crates/boring/src/sign.rs @@ -0,0 +1,680 @@ +//! Message signatures. +//! +//! The `Signer` allows for the computation of cryptographic signatures of +//! data given a private key. The `Verifier` can then be used with the +//! corresponding public key to verify the integrity and authenticity of that +//! data given the signature. +//! +//! # Examples +//! +//! Sign and verify data given an RSA keypair: +//! +//! ```rust +//! use boring::sign::{Signer, Verifier}; +//! use boring::rsa::Rsa; +//! use boring::pkey::PKey; +//! use boring::hash::MessageDigest; +//! +//! // Generate a keypair +//! let keypair = Rsa::generate(2048).unwrap(); +//! let keypair = PKey::from_rsa(keypair).unwrap(); +//! +//! let data = b"hello, world!"; +//! let data2 = b"hola, mundo!"; +//! +//! // Sign the data +//! let mut signer = Signer::new(MessageDigest::sha256(), &keypair).unwrap(); +//! signer.update(data).unwrap(); +//! signer.update(data2).unwrap(); +//! let signature = signer.sign_to_vec().unwrap(); +//! +//! // Verify the data +//! let mut verifier = Verifier::new(MessageDigest::sha256(), &keypair).unwrap(); +//! verifier.update(data).unwrap(); +//! verifier.update(data2).unwrap(); +//! assert!(verifier.verify(&signature).unwrap()); +//! ``` +use crate::ffi; +use foreign_types::ForeignTypeRef; +use libc::c_int; +use std::io::{self, Write}; +use std::marker::PhantomData; +use std::ptr; + +use crate::error::ErrorStack; +use crate::hash::MessageDigest; +use crate::pkey::{HasPrivate, HasPublic, PKeyRef}; +use crate::rsa::Padding; +use crate::{cvt, cvt_p}; + +use crate::ffi::{EVP_MD_CTX_free, EVP_MD_CTX_new}; + +/// Salt lengths that must be used with `set_rsa_pss_saltlen`. +pub struct RsaPssSaltlen(c_int); + +impl RsaPssSaltlen { + /// Returns the integer representation of `RsaPssSaltlen`. + fn as_raw(&self) -> c_int { + self.0 + } + + /// Sets the salt length to the given value. + pub fn custom(val: c_int) -> RsaPssSaltlen { + RsaPssSaltlen(val) + } + + /// The salt length is set to the digest length. + /// Corresponds to the special value `-1`. + pub const DIGEST_LENGTH: RsaPssSaltlen = RsaPssSaltlen(-1); + /// The salt length is set to the maximum permissible value. + /// Corresponds to the special value `-2`. + pub const MAXIMUM_LENGTH: RsaPssSaltlen = RsaPssSaltlen(-2); +} + +/// A type which computes cryptographic signatures of data. +pub struct Signer<'a> { + md_ctx: *mut ffi::EVP_MD_CTX, + pctx: *mut ffi::EVP_PKEY_CTX, + _p: PhantomData<&'a ()>, +} + +unsafe impl<'a> Sync for Signer<'a> {} +unsafe impl<'a> Send for Signer<'a> {} + +impl<'a> Drop for Signer<'a> { + fn drop(&mut self) { + // pkey_ctx is owned by the md_ctx, so no need to explicitly free it. + unsafe { + EVP_MD_CTX_free(self.md_ctx); + } + } +} + +#[allow(clippy::len_without_is_empty)] +impl<'a> Signer<'a> { + /// Creates a new `Signer`. + /// + /// This cannot be used with Ed25519 or Ed448 keys. Please refer to + /// `new_without_digest`. + /// + /// OpenSSL documentation at [`EVP_DigestSignInit`]. + /// + /// [`EVP_DigestSignInit`]: https://www.openssl.org/docs/manmaster/man3/EVP_DigestSignInit.html + pub fn new(type_: MessageDigest, pkey: &'a PKeyRef) -> Result, ErrorStack> + where + T: HasPrivate, + { + Self::new_intern(Some(type_), pkey) + } + + /// Creates a new `Signer` without a digest. + /// + /// This is the only way to create a `Verifier` for Ed25519 or Ed448 keys. + /// It can also be used to create a CMAC. + /// + /// OpenSSL documentation at [`EVP_DigestSignInit`]. + /// + /// [`EVP_DigestSignInit`]: https://www.openssl.org/docs/manmaster/man3/EVP_DigestSignInit.html + pub fn new_without_digest(pkey: &'a PKeyRef) -> Result, ErrorStack> + where + T: HasPrivate, + { + Self::new_intern(None, pkey) + } + + fn new_intern( + type_: Option, + pkey: &'a PKeyRef, + ) -> Result, ErrorStack> + where + T: HasPrivate, + { + unsafe { + ffi::init(); + + let ctx = cvt_p(EVP_MD_CTX_new())?; + let mut pctx: *mut ffi::EVP_PKEY_CTX = ptr::null_mut(); + let r = ffi::EVP_DigestSignInit( + ctx, + &mut pctx, + type_.map(|t| t.as_ptr()).unwrap_or(ptr::null()), + ptr::null_mut(), + pkey.as_ptr(), + ); + if r != 1 { + EVP_MD_CTX_free(ctx); + return Err(ErrorStack::get()); + } + + assert!(!pctx.is_null()); + + Ok(Signer { + md_ctx: ctx, + pctx, + _p: PhantomData, + }) + } + } + + /// Returns the RSA padding mode in use. + /// + /// This is only useful for RSA keys. + /// + /// This corresponds to `EVP_PKEY_CTX_get_rsa_padding`. + pub fn rsa_padding(&self) -> Result { + unsafe { + let mut pad = 0; + cvt(ffi::EVP_PKEY_CTX_get_rsa_padding(self.pctx, &mut pad)) + .map(|_| Padding::from_raw(pad)) + } + } + + /// Sets the RSA padding mode. + /// + /// This is only useful for RSA keys. + /// + /// This corresponds to [`EVP_PKEY_CTX_set_rsa_padding`]. + /// + /// [`EVP_PKEY_CTX_set_rsa_padding`]: https://www.openssl.org/docs/man1.1.0/crypto/EVP_PKEY_CTX_set_rsa_padding.html + pub fn set_rsa_padding(&mut self, padding: Padding) -> Result<(), ErrorStack> { + unsafe { + cvt(ffi::EVP_PKEY_CTX_set_rsa_padding( + self.pctx, + padding.as_raw(), + )) + .map(|_| ()) + } + } + + /// Sets the RSA PSS salt length. + /// + /// This is only useful for RSA keys. + /// + /// This corresponds to [`EVP_PKEY_CTX_set_rsa_pss_saltlen`]. + /// + /// [`EVP_PKEY_CTX_set_rsa_pss_saltlen`]: https://www.openssl.org/docs/man1.1.0/crypto/EVP_PKEY_CTX_set_rsa_pss_saltlen.html + pub fn set_rsa_pss_saltlen(&mut self, len: RsaPssSaltlen) -> Result<(), ErrorStack> { + unsafe { + cvt(ffi::EVP_PKEY_CTX_set_rsa_pss_saltlen( + self.pctx, + len.as_raw(), + )) + .map(|_| ()) + } + } + + /// Sets the RSA MGF1 algorithm. + /// + /// This is only useful for RSA keys. + /// + /// This corresponds to [`EVP_PKEY_CTX_set_rsa_mgf1_md`]. + /// + /// [`EVP_PKEY_CTX_set_rsa_mgf1_md`]: https://www.openssl.org/docs/manmaster/man7/RSA-PSS.html + pub fn set_rsa_mgf1_md(&mut self, md: MessageDigest) -> Result<(), ErrorStack> { + unsafe { + cvt(ffi::EVP_PKEY_CTX_set_rsa_mgf1_md( + self.pctx, + md.as_ptr() as *mut _, + )) + .map(|_| ()) + } + } + + /// Feeds more data into the `Signer`. + /// + /// Please note that PureEdDSA (Ed25519 and Ed448 keys) do not support streaming. + /// Use `sign_oneshot` instead. + /// + /// OpenSSL documentation at [`EVP_DigestUpdate`]. + /// + /// [`EVP_DigestUpdate`]: https://www.openssl.org/docs/manmaster/man3/EVP_DigestInit.html + pub fn update(&mut self, buf: &[u8]) -> Result<(), ErrorStack> { + unsafe { + cvt(ffi::EVP_DigestUpdate( + self.md_ctx, + buf.as_ptr() as *const _, + buf.len(), + )) + .map(|_| ()) + } + } + + /// Computes an upper bound on the signature length. + /// + /// The actual signature may be shorter than this value. Check the return value of + /// `sign` to get the exact length. + /// + /// OpenSSL documentation at [`EVP_DigestSignFinal`]. + /// + /// [`EVP_DigestSignFinal`]: https://www.openssl.org/docs/man1.1.0/crypto/EVP_DigestSignFinal.html + pub fn len(&self) -> Result { + self.len_intern() + } + + fn len_intern(&self) -> Result { + unsafe { + let mut len = 0; + cvt(ffi::EVP_DigestSign( + self.md_ctx, + ptr::null_mut(), + &mut len, + ptr::null(), + 0, + ))?; + Ok(len) + } + } + + /// Writes the signature into the provided buffer, returning the number of bytes written. + /// + /// This method will fail if the buffer is not large enough for the signature. Use the `len` + /// method to get an upper bound on the required size. + /// + /// OpenSSL documentation at [`EVP_DigestSignFinal`]. + /// + /// [`EVP_DigestSignFinal`]: https://www.openssl.org/docs/man1.1.0/crypto/EVP_DigestSignFinal.html + pub fn sign(&self, buf: &mut [u8]) -> Result { + unsafe { + let mut len = buf.len(); + cvt(ffi::EVP_DigestSignFinal( + self.md_ctx, + buf.as_mut_ptr() as *mut _, + &mut len, + ))?; + Ok(len) + } + } + + /// Returns the signature. + /// + /// This is a simple convenience wrapper over `len` and `sign`. + pub fn sign_to_vec(&self) -> Result, ErrorStack> { + let mut buf = vec![0; self.len()?]; + let len = self.sign(&mut buf)?; + // The advertised length is not always equal to the real length for things like DSA + buf.truncate(len); + Ok(buf) + } + + /// Signs the data in data_buf and writes the signature into the buffer sig_buf, returning the + /// number of bytes written. + /// + /// For PureEdDSA (Ed25519 and Ed448 keys) this is the only way to sign data. + /// + /// This method will fail if the buffer is not large enough for the signature. Use the `len` + /// method to get an upper bound on the required size. + /// + /// OpenSSL documentation at [`EVP_DigestSign`]. + /// + /// [`EVP_DigestSign`]: https://www.openssl.org/docs/man1.1.1/man3/EVP_DigestSign.html + pub fn sign_oneshot( + &mut self, + sig_buf: &mut [u8], + data_buf: &[u8], + ) -> Result { + unsafe { + let mut sig_len = sig_buf.len(); + cvt(ffi::EVP_DigestSign( + self.md_ctx, + sig_buf.as_mut_ptr() as *mut _, + &mut sig_len, + data_buf.as_ptr() as *const _, + data_buf.len(), + ))?; + Ok(sig_len) + } + } + + /// Returns the signature. + /// + /// This is a simple convenience wrapper over `len` and `sign_oneshot`. + pub fn sign_oneshot_to_vec(&mut self, data_buf: &[u8]) -> Result, ErrorStack> { + let mut sig_buf = vec![0; self.len()?]; + let len = self.sign_oneshot(&mut sig_buf, data_buf)?; + // The advertised length is not always equal to the real length for things like DSA + sig_buf.truncate(len); + Ok(sig_buf) + } +} + +impl<'a> Write for Signer<'a> { + fn write(&mut self, buf: &[u8]) -> io::Result { + self.update(buf)?; + Ok(buf.len()) + } + + fn flush(&mut self) -> io::Result<()> { + Ok(()) + } +} + +pub struct Verifier<'a> { + md_ctx: *mut ffi::EVP_MD_CTX, + pctx: *mut ffi::EVP_PKEY_CTX, + pkey_pd: PhantomData<&'a ()>, +} + +unsafe impl<'a> Sync for Verifier<'a> {} +unsafe impl<'a> Send for Verifier<'a> {} + +impl<'a> Drop for Verifier<'a> { + fn drop(&mut self) { + // pkey_ctx is owned by the md_ctx, so no need to explicitly free it. + unsafe { + EVP_MD_CTX_free(self.md_ctx); + } + } +} + +/// A type which verifies cryptographic signatures of data. +impl<'a> Verifier<'a> { + /// Creates a new `Verifier`. + /// + /// This cannot be used with Ed25519 or Ed448 keys. Please refer to + /// `new_without_digest`. + /// + /// OpenSSL documentation at [`EVP_DigestVerifyInit`]. + /// + /// [`EVP_DigestVerifyInit`]: https://www.openssl.org/docs/manmaster/man3/EVP_DigestVerifyInit.html + pub fn new(type_: MessageDigest, pkey: &'a PKeyRef) -> Result, ErrorStack> + where + T: HasPublic, + { + Verifier::new_intern(Some(type_), pkey) + } + + /// Creates a new `Verifier` without a digest. + /// + /// This is the only way to create a `Verifier` for Ed25519 or Ed448 keys. + /// + /// OpenSSL documentation at [`EVP_DigestVerifyInit`]. + /// + /// [`EVP_DigestVerifyInit`]: https://www.openssl.org/docs/manmaster/man3/EVP_DigestVerifyInit.html + pub fn new_without_digest(pkey: &'a PKeyRef) -> Result, ErrorStack> + where + T: HasPublic, + { + Verifier::new_intern(None, pkey) + } + + fn new_intern( + type_: Option, + pkey: &'a PKeyRef, + ) -> Result, ErrorStack> + where + T: HasPublic, + { + unsafe { + ffi::init(); + + let ctx = cvt_p(EVP_MD_CTX_new())?; + let mut pctx: *mut ffi::EVP_PKEY_CTX = ptr::null_mut(); + let r = ffi::EVP_DigestVerifyInit( + ctx, + &mut pctx, + type_.map(|t| t.as_ptr()).unwrap_or(ptr::null()), + ptr::null_mut(), + pkey.as_ptr(), + ); + if r != 1 { + EVP_MD_CTX_free(ctx); + return Err(ErrorStack::get()); + } + + assert!(!pctx.is_null()); + + Ok(Verifier { + md_ctx: ctx, + pctx, + pkey_pd: PhantomData, + }) + } + } + + /// Returns the RSA padding mode in use. + /// + /// This is only useful for RSA keys. + /// + /// This corresponds to `EVP_PKEY_CTX_get_rsa_padding`. + pub fn rsa_padding(&self) -> Result { + unsafe { + let mut pad = 0; + cvt(ffi::EVP_PKEY_CTX_get_rsa_padding(self.pctx, &mut pad)) + .map(|_| Padding::from_raw(pad)) + } + } + + /// Sets the RSA padding mode. + /// + /// This is only useful for RSA keys. + /// + /// This corresponds to [`EVP_PKEY_CTX_set_rsa_padding`]. + /// + /// [`EVP_PKEY_CTX_set_rsa_padding`]: https://www.openssl.org/docs/man1.1.0/crypto/EVP_PKEY_CTX_set_rsa_padding.html + pub fn set_rsa_padding(&mut self, padding: Padding) -> Result<(), ErrorStack> { + unsafe { + cvt(ffi::EVP_PKEY_CTX_set_rsa_padding( + self.pctx, + padding.as_raw(), + )) + .map(|_| ()) + } + } + + /// Sets the RSA PSS salt length. + /// + /// This is only useful for RSA keys. + /// + /// This corresponds to [`EVP_PKEY_CTX_set_rsa_pss_saltlen`]. + /// + /// [`EVP_PKEY_CTX_set_rsa_pss_saltlen`]: https://www.openssl.org/docs/man1.1.0/crypto/EVP_PKEY_CTX_set_rsa_pss_saltlen.html + pub fn set_rsa_pss_saltlen(&mut self, len: RsaPssSaltlen) -> Result<(), ErrorStack> { + unsafe { + cvt(ffi::EVP_PKEY_CTX_set_rsa_pss_saltlen( + self.pctx, + len.as_raw(), + )) + .map(|_| ()) + } + } + + /// Sets the RSA MGF1 algorithm. + /// + /// This is only useful for RSA keys. + /// + /// This corresponds to [`EVP_PKEY_CTX_set_rsa_mgf1_md`]. + /// + /// [`EVP_PKEY_CTX_set_rsa_mgf1_md`]: https://www.openssl.org/docs/manmaster/man7/RSA-PSS.html + pub fn set_rsa_mgf1_md(&mut self, md: MessageDigest) -> Result<(), ErrorStack> { + unsafe { + cvt(ffi::EVP_PKEY_CTX_set_rsa_mgf1_md( + self.pctx, + md.as_ptr() as *mut _, + )) + .map(|_| ()) + } + } + + /// Feeds more data into the `Verifier`. + /// + /// Please note that PureEdDSA (Ed25519 and Ed448 keys) do not support streaming. + /// Use `verify_oneshot` instead. + /// + /// OpenSSL documentation at [`EVP_DigestUpdate`]. + /// + /// [`EVP_DigestUpdate`]: https://www.openssl.org/docs/manmaster/man3/EVP_DigestInit.html + pub fn update(&mut self, buf: &[u8]) -> Result<(), ErrorStack> { + unsafe { + cvt(ffi::EVP_DigestUpdate( + self.md_ctx, + buf.as_ptr() as *const _, + buf.len(), + )) + .map(|_| ()) + } + } + + /// Determines if the data fed into the `Verifier` matches the provided signature. + /// + /// OpenSSL documentation at [`EVP_DigestVerifyFinal`]. + /// + /// [`EVP_DigestVerifyFinal`]: https://www.openssl.org/docs/manmaster/man3/EVP_DigestVerifyFinal.html + pub fn verify(&self, signature: &[u8]) -> Result { + unsafe { + let r = + EVP_DigestVerifyFinal(self.md_ctx, signature.as_ptr() as *mut _, signature.len()); + match r { + 1 => Ok(true), + 0 => { + ErrorStack::get(); // discard error stack + Ok(false) + } + _ => Err(ErrorStack::get()), + } + } + } + + /// Determines if the data given in buf matches the provided signature. + /// + /// OpenSSL documentation at [`EVP_DigestVerify`]. + /// + /// [`EVP_DigestVerify`]: https://www.openssl.org/docs/man1.1.1/man3/EVP_DigestVerify.html + pub fn verify_oneshot(&mut self, signature: &[u8], buf: &[u8]) -> Result { + unsafe { + let r = ffi::EVP_DigestVerify( + self.md_ctx, + signature.as_ptr() as *const _, + signature.len(), + buf.as_ptr() as *const _, + buf.len(), + ); + match r { + 1 => Ok(true), + 0 => { + ErrorStack::get(); + Ok(false) + } + _ => Err(ErrorStack::get()), + } + } + } +} + +impl<'a> Write for Verifier<'a> { + fn write(&mut self, buf: &[u8]) -> io::Result { + self.update(buf)?; + Ok(buf.len()) + } + + fn flush(&mut self) -> io::Result<()> { + Ok(()) + } +} + +use crate::ffi::EVP_DigestVerifyFinal; + +#[cfg(test)] +mod test { + use super::RsaPssSaltlen; + use hex::{self, FromHex}; + + use crate::ec::{EcGroup, EcKey}; + use crate::hash::MessageDigest; + use crate::nid::Nid; + use crate::pkey::PKey; + use crate::rsa::{Padding, Rsa}; + use crate::sign::{Signer, Verifier}; + + const INPUT: &str = + "65794a68624763694f694a53557a49314e694a392e65794a7063334d694f694a71623255694c41304b49434a6c\ + 654841694f6a457a4d4441344d546b7a4f44417344516f67496d6830644841364c79396c654746746347786c4c\ + 6d4e76625339706331397962323930496a7030636e566c6651"; + + const SIGNATURE: &str = + "702e218943e88fd11eb5d82dbf7845f34106ae1b81fff7731116add1717d83656d420afd3c96eedd73a2663e51\ + 66687b000b87226e0187ed1073f945e582adfcef16d85a798ee8c66ddb3db8975b17d09402beedd5d9d9700710\ + 8db28160d5f8040ca7445762b81fbe7ff9d92e0ae76f24f25b33bbe6f44ae61eb1040acb20044d3ef9128ed401\ + 30795bd4bd3b41eecad066ab651981fde48df77f372dc38b9fafdd3befb18b5da3cc3c2eb02f9e3a41d612caad\ + 15911273a05f23b9e838faaf849d698429ef5a1e88798236c3d40e604522a544c8f27a7a2db80663d16cf7caea\ + 56de405cb2215a45b2c25566b55ac1a748a070dfc8a32a469543d019eefb47"; + + #[test] + fn rsa_sign() { + let key = include_bytes!("../test/rsa.pem"); + let private_key = Rsa::private_key_from_pem(key).unwrap(); + let pkey = PKey::from_rsa(private_key).unwrap(); + + let mut signer = Signer::new(MessageDigest::sha256(), &pkey).unwrap(); + assert_eq!(signer.rsa_padding().unwrap(), Padding::PKCS1); + signer.set_rsa_padding(Padding::PKCS1).unwrap(); + signer.update(&Vec::from_hex(INPUT).unwrap()).unwrap(); + let result = signer.sign_to_vec().unwrap(); + + assert_eq!(hex::encode(result), SIGNATURE); + } + + #[test] + fn rsa_verify_ok() { + let key = include_bytes!("../test/rsa.pem"); + let private_key = Rsa::private_key_from_pem(key).unwrap(); + let pkey = PKey::from_rsa(private_key).unwrap(); + + let mut verifier = Verifier::new(MessageDigest::sha256(), &pkey).unwrap(); + assert_eq!(verifier.rsa_padding().unwrap(), Padding::PKCS1); + verifier.update(&Vec::from_hex(INPUT).unwrap()).unwrap(); + assert!(verifier.verify(&Vec::from_hex(SIGNATURE).unwrap()).unwrap()); + } + + #[test] + fn rsa_verify_invalid() { + let key = include_bytes!("../test/rsa.pem"); + let private_key = Rsa::private_key_from_pem(key).unwrap(); + let pkey = PKey::from_rsa(private_key).unwrap(); + + let mut verifier = Verifier::new(MessageDigest::sha256(), &pkey).unwrap(); + verifier.update(&Vec::from_hex(INPUT).unwrap()).unwrap(); + verifier.update(b"foobar").unwrap(); + assert!(!verifier.verify(&Vec::from_hex(SIGNATURE).unwrap()).unwrap()); + } + + #[test] + fn ec() { + let group = EcGroup::from_curve_name(Nid::X9_62_PRIME256V1).unwrap(); + let key = EcKey::generate(&group).unwrap(); + let key = PKey::from_ec_key(key).unwrap(); + + let mut signer = Signer::new(MessageDigest::sha256(), &key).unwrap(); + signer.update(b"hello world").unwrap(); + let signature = signer.sign_to_vec().unwrap(); + + let mut verifier = Verifier::new(MessageDigest::sha256(), &key).unwrap(); + verifier.update(b"hello world").unwrap(); + assert!(verifier.verify(&signature).unwrap()); + } + + #[test] + fn rsa_sign_verify() { + let key = include_bytes!("../test/rsa.pem"); + let private_key = Rsa::private_key_from_pem(key).unwrap(); + let pkey = PKey::from_rsa(private_key).unwrap(); + + let mut signer = Signer::new(MessageDigest::sha256(), &pkey).unwrap(); + signer.set_rsa_padding(Padding::PKCS1_PSS).unwrap(); + assert_eq!(signer.rsa_padding().unwrap(), Padding::PKCS1_PSS); + signer + .set_rsa_pss_saltlen(RsaPssSaltlen::DIGEST_LENGTH) + .unwrap(); + signer.set_rsa_mgf1_md(MessageDigest::sha256()).unwrap(); + signer.update(&Vec::from_hex(INPUT).unwrap()).unwrap(); + let signature = signer.sign_to_vec().unwrap(); + + let mut verifier = Verifier::new(MessageDigest::sha256(), &pkey).unwrap(); + verifier.set_rsa_padding(Padding::PKCS1_PSS).unwrap(); + verifier + .set_rsa_pss_saltlen(RsaPssSaltlen::DIGEST_LENGTH) + .unwrap(); + verifier.set_rsa_mgf1_md(MessageDigest::sha256()).unwrap(); + verifier.update(&Vec::from_hex(INPUT).unwrap()).unwrap(); + assert!(verifier.verify(&signature).unwrap()); + } +} diff --git a/crates/boring/src/srtp.rs b/crates/boring/src/srtp.rs new file mode 100644 index 000000000..84deca9e3 --- /dev/null +++ b/crates/boring/src/srtp.rs @@ -0,0 +1,59 @@ +use crate::ffi; +use crate::stack::Stackable; +use foreign_types::ForeignTypeRef; +use libc::c_ulong; +use std::ffi::CStr; +use std::str; + +/// fake free method, since SRTP_PROTECTION_PROFILE is static +unsafe fn free(_profile: *mut ffi::SRTP_PROTECTION_PROFILE) {} + +foreign_type_and_impl_send_sync! { + type CType = ffi::SRTP_PROTECTION_PROFILE; + fn drop = free; + + pub struct SrtpProtectionProfile; +} + +impl Stackable for SrtpProtectionProfile { + type StackType = ffi::stack_st_SRTP_PROTECTION_PROFILE; +} + +impl SrtpProtectionProfileRef { + pub fn id(&self) -> SrtpProfileId { + SrtpProfileId::from_raw(unsafe { (*self.as_ptr()).id }) + } + pub fn name(&self) -> &'static str { + unsafe { CStr::from_ptr((*self.as_ptr()).name as *const _) } + .to_str() + .expect("should be UTF-8") + } +} + +/// An identifier of an SRTP protection profile. +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +pub struct SrtpProfileId(c_ulong); + +impl SrtpProfileId { + pub const SRTP_AES128_CM_SHA1_80: SrtpProfileId = + SrtpProfileId(ffi::SRTP_AES128_CM_SHA1_80 as _); + pub const SRTP_AES128_CM_SHA1_32: SrtpProfileId = + SrtpProfileId(ffi::SRTP_AES128_CM_SHA1_32 as _); + pub const SRTP_AES128_F8_SHA1_80: SrtpProfileId = + SrtpProfileId(ffi::SRTP_AES128_F8_SHA1_80 as _); + pub const SRTP_AES128_F8_SHA1_32: SrtpProfileId = + SrtpProfileId(ffi::SRTP_AES128_F8_SHA1_32 as _); + pub const SRTP_NULL_SHA1_80: SrtpProfileId = SrtpProfileId(ffi::SRTP_NULL_SHA1_80 as _); + pub const SRTP_NULL_SHA1_32: SrtpProfileId = SrtpProfileId(ffi::SRTP_NULL_SHA1_32 as _); + + /// Creates a `SrtpProfileId` from an integer representation. + pub fn from_raw(value: c_ulong) -> SrtpProfileId { + SrtpProfileId(value) + } + + /// Returns the integer representation of `SrtpProfileId`. + #[allow(clippy::trivially_copy_pass_by_ref)] + pub fn as_raw(&self) -> c_ulong { + self.0 + } +} diff --git a/crates/boring/src/ssl/bio.rs b/crates/boring/src/ssl/bio.rs new file mode 100644 index 000000000..b88fdaff0 --- /dev/null +++ b/crates/boring/src/ssl/bio.rs @@ -0,0 +1,246 @@ +use crate::ffi::{ + self, BIO_clear_retry_flags, BIO_new, BIO_set_retry_read, BIO_set_retry_write, BIO, + BIO_CTRL_DGRAM_QUERY_MTU, BIO_CTRL_FLUSH, +}; +use libc::{c_char, c_int, c_long, c_void, strlen}; +use std::any::Any; +use std::io; +use std::io::prelude::*; +use std::panic::{catch_unwind, AssertUnwindSafe}; +use std::ptr; +use std::slice; + +use crate::cvt_p; +use crate::error::ErrorStack; + +pub struct StreamState { + pub stream: S, + pub error: Option, + pub panic: Option>, + pub dtls_mtu_size: c_long, +} + +/// Safe wrapper for BIO_METHOD +pub struct BioMethod(BIO_METHOD); + +impl BioMethod { + fn new() -> BioMethod { + BioMethod(BIO_METHOD::new::()) + } +} + +unsafe impl Sync for BioMethod {} +unsafe impl Send for BioMethod {} + +pub fn new(stream: S) -> Result<(*mut BIO, BioMethod), ErrorStack> { + let method = BioMethod::new::(); + + let state = Box::new(StreamState { + stream, + error: None, + panic: None, + dtls_mtu_size: 0, + }); + + unsafe { + let bio = cvt_p(BIO_new(method.0.get()))?; + BIO_set_data(bio, Box::into_raw(state) as *mut _); + BIO_set_init(bio, 1); + + Ok((bio, method)) + } +} + +pub unsafe fn take_error(bio: *mut BIO) -> Option { + let state = state::(bio); + state.error.take() +} + +pub unsafe fn take_panic(bio: *mut BIO) -> Option> { + let state = state::(bio); + state.panic.take() +} + +pub unsafe fn get_ref<'a, S: 'a>(bio: *mut BIO) -> &'a S { + &state(bio).stream +} + +pub unsafe fn get_mut<'a, S: 'a>(bio: *mut BIO) -> &'a mut S { + &mut state(bio).stream +} + +pub unsafe extern "C" fn take_stream(bio: *mut BIO) -> S { + assert!(!bio.is_null()); + + let data = BIO_get_data(bio); + + assert!(!data.is_null()); + + let state = Box::>::from_raw(data as *mut _); + + BIO_set_data(bio, ptr::null_mut()); + + state.stream +} + +pub unsafe fn set_dtls_mtu_size(bio: *mut BIO, mtu_size: usize) { + if mtu_size as u64 > c_long::max_value() as u64 { + panic!( + "Given MTU size {} can't be represented in a positive `c_long` range", + mtu_size + ) + } + state::(bio).dtls_mtu_size = mtu_size as c_long; +} + +unsafe fn state<'a, S: 'a>(bio: *mut BIO) -> &'a mut StreamState { + let data = BIO_get_data(bio) as *mut StreamState; + + assert!(!data.is_null()); + + &mut *data +} + +unsafe extern "C" fn bwrite(bio: *mut BIO, buf: *const c_char, len: c_int) -> c_int { + BIO_clear_retry_flags(bio); + + let state = state::(bio); + let buf = slice::from_raw_parts(buf as *const _, len as usize); + + match catch_unwind(AssertUnwindSafe(|| state.stream.write(buf))) { + Ok(Ok(len)) => len as c_int, + Ok(Err(err)) => { + if retriable_error(&err) { + BIO_set_retry_write(bio); + } + state.error = Some(err); + -1 + } + Err(err) => { + state.panic = Some(err); + -1 + } + } +} + +unsafe extern "C" fn bread(bio: *mut BIO, buf: *mut c_char, len: c_int) -> c_int { + BIO_clear_retry_flags(bio); + + let state = state::(bio); + let buf = slice::from_raw_parts_mut(buf as *mut _, len as usize); + + match catch_unwind(AssertUnwindSafe(|| state.stream.read(buf))) { + Ok(Ok(len)) => len as c_int, + Ok(Err(err)) => { + if retriable_error(&err) { + BIO_set_retry_read(bio); + } + state.error = Some(err); + -1 + } + Err(err) => { + state.panic = Some(err); + -1 + } + } +} + +#[allow(clippy::match_like_matches_macro)] // matches macro requires rust 1.42.0 +fn retriable_error(err: &io::Error) -> bool { + match err.kind() { + io::ErrorKind::WouldBlock | io::ErrorKind::NotConnected => true, + _ => false, + } +} + +unsafe extern "C" fn bputs(bio: *mut BIO, s: *const c_char) -> c_int { + bwrite::(bio, s, strlen(s) as c_int) +} + +unsafe extern "C" fn ctrl( + bio: *mut BIO, + cmd: c_int, + _num: c_long, + _ptr: *mut c_void, +) -> c_long { + let state = state::(bio); + + if cmd == BIO_CTRL_FLUSH { + match catch_unwind(AssertUnwindSafe(|| state.stream.flush())) { + Ok(Ok(())) => 1, + Ok(Err(err)) => { + state.error = Some(err); + 0 + } + Err(err) => { + state.panic = Some(err); + 0 + } + } + } else if cmd == BIO_CTRL_DGRAM_QUERY_MTU { + state.dtls_mtu_size + } else { + 0 + } +} + +unsafe extern "C" fn create(bio: *mut BIO) -> c_int { + BIO_set_init(bio, 0); + BIO_set_num(bio, 0); + BIO_set_data(bio, ptr::null_mut()); + BIO_set_flags(bio, 0); + 1 +} + +unsafe extern "C" fn destroy(bio: *mut BIO) -> c_int { + if bio.is_null() { + return 0; + } + + let data = BIO_get_data(bio); + + if !data.is_null() { + drop(Box::>::from_raw(data as *mut _)); + BIO_set_data(bio, ptr::null_mut()); + } + + BIO_set_init(bio, 0); + 1 +} + +use crate::ffi::{BIO_get_data, BIO_set_data, BIO_set_flags, BIO_set_init}; + +#[allow(bad_style)] +unsafe fn BIO_set_num(_bio: *mut ffi::BIO, _num: c_int) {} + +#[allow(bad_style, clippy::upper_case_acronyms)] +struct BIO_METHOD(*mut ffi::BIO_METHOD); + +impl BIO_METHOD { + fn new() -> BIO_METHOD { + unsafe { + let ptr = ffi::BIO_meth_new(ffi::BIO_TYPE_NONE, b"rust\0".as_ptr() as *const _); + assert!(!ptr.is_null()); + let ret = BIO_METHOD(ptr); + assert!(ffi::BIO_meth_set_write(ptr, Some(bwrite::)) != 0); + assert!(ffi::BIO_meth_set_read(ptr, Some(bread::)) != 0); + assert!(ffi::BIO_meth_set_puts(ptr, Some(bputs::)) != 0); + assert!(ffi::BIO_meth_set_ctrl(ptr, Some(ctrl::)) != 0); + assert!(ffi::BIO_meth_set_create(ptr, Some(create)) != 0); + assert!(ffi::BIO_meth_set_destroy(ptr, Some(destroy::)) != 0); + ret + } + } + + fn get(&self) -> *mut ffi::BIO_METHOD { + self.0 + } +} + +impl Drop for BIO_METHOD { + fn drop(&mut self) { + unsafe { + ffi::BIO_meth_free(self.0); + } + } +} diff --git a/crates/boring/src/ssl/callbacks.rs b/crates/boring/src/ssl/callbacks.rs new file mode 100644 index 000000000..c4bcb3f05 --- /dev/null +++ b/crates/boring/src/ssl/callbacks.rs @@ -0,0 +1,463 @@ +#![forbid(unsafe_op_in_unsafe_fn)] + +use super::{ + AlpnError, ClientHello, GetSessionPendingError, PrivateKeyMethod, PrivateKeyMethodError, + SelectCertError, SniError, Ssl, SslAlert, SslContext, SslContextRef, SslRef, SslSession, + SslSessionRef, SslSignatureAlgorithm, SESSION_CTX_INDEX, +}; +use crate::error::ErrorStack; +use crate::ffi; +use crate::x509::{X509StoreContext, X509StoreContextRef}; +use foreign_types::ForeignType; +use foreign_types::ForeignTypeRef; +use libc::c_char; +use libc::{c_int, c_uchar, c_uint, c_void}; +use std::ffi::CStr; +use std::ptr; +use std::slice; +use std::str; +use std::sync::Arc; + +pub extern "C" fn raw_verify(preverify_ok: c_int, x509_ctx: *mut ffi::X509_STORE_CTX) -> c_int +where + F: Fn(bool, &mut X509StoreContextRef) -> bool + 'static + Sync + Send, +{ + // SAFETY: boring provides valid inputs. + let ctx = unsafe { X509StoreContextRef::from_ptr_mut(x509_ctx) }; + + let ssl_idx = X509StoreContext::ssl_idx().expect("BUG: store context ssl index missing"); + let verify_idx = SslContext::cached_ex_index::(); + + let verify = ctx + .ex_data(ssl_idx) + .expect("BUG: store context missing ssl") + .ssl_context() + .ex_data(verify_idx) + .expect("BUG: verify callback missing"); + + // SAFETY: The callback won't outlive the context it's associated with + // because there is no `X509StoreContextRef::ssl_mut(&mut self)` method. + let verify = unsafe { &*(verify as *const F) }; + + verify(preverify_ok != 0, ctx) as c_int +} + +pub(super) unsafe extern "C" fn raw_client_psk( + ssl_ptr: *mut ffi::SSL, + hint: *const c_char, + identity: *mut c_char, + max_identity_len: c_uint, + psk: *mut c_uchar, + max_psk_len: c_uint, +) -> c_uint +where + F: Fn(&mut SslRef, Option<&[u8]>, &mut [u8], &mut [u8]) -> Result + + 'static + + Sync + + Send, +{ + // SAFETY: boring provides valid inputs. + + let ssl = unsafe { SslRef::from_ptr_mut(ssl_ptr) }; + + let hint = if hint.is_null() { + None + } else { + Some(unsafe { CStr::from_ptr(hint) }.to_bytes()) + }; + + // Give the callback mutable slices into which it can write the identity and psk. + let identity_sl = + unsafe { slice::from_raw_parts_mut(identity as *mut u8, max_identity_len as usize) }; + let psk_sl = unsafe { slice::from_raw_parts_mut(psk, max_psk_len as usize) }; + + let ssl_context = ssl.ssl_context().to_owned(); + let callback = ssl_context + .ex_data(SslContext::cached_ex_index::()) + .expect("BUG: psk callback missing"); + + match callback(ssl, hint, identity_sl, psk_sl) { + Ok(psk_len) => psk_len as u32, + Err(e) => { + e.put(); + 0 + } + } +} + +pub(super) unsafe extern "C" fn raw_server_psk( + ssl: *mut ffi::SSL, + identity: *const c_char, + psk: *mut c_uchar, + max_psk_len: c_uint, +) -> c_uint +where + F: Fn(&mut SslRef, Option<&[u8]>, &mut [u8]) -> Result + + 'static + + Sync + + Send, +{ + // SAFETY: boring provides valid inputs. + + let ssl = unsafe { SslRef::from_ptr_mut(ssl) }; + + let identity = if identity.is_null() { + None + } else { + Some(unsafe { CStr::from_ptr(identity) }.to_bytes()) + }; + + // Give the callback mutable slices into which it can write the psk. + let psk_sl = unsafe { slice::from_raw_parts_mut(psk, max_psk_len as usize) }; + + let ssl_context = ssl.ssl_context().to_owned(); + let callback = ssl_context + .ex_data(SslContext::cached_ex_index::()) + .expect("BUG: psk callback missing"); + + match callback(ssl, identity, psk_sl) { + Ok(psk_len) => psk_len as u32, + Err(e) => { + e.put(); + 0 + } + } +} + +pub(super) unsafe extern "C" fn ssl_raw_verify( + preverify_ok: c_int, + x509_ctx: *mut ffi::X509_STORE_CTX, +) -> c_int +where + F: Fn(bool, &mut X509StoreContextRef) -> bool + 'static + Sync + Send, +{ + // SAFETY: boring provides valid inputs. + let ctx = unsafe { X509StoreContextRef::from_ptr_mut(x509_ctx) }; + + let ssl_idx = X509StoreContext::ssl_idx().expect("BUG: store context ssl index missing"); + + // NOTE(nox): I'm pretty sure this Arc is unnecessary here as there is + // no way to get a `&mut SslRef` from a `&mut X509StoreContextRef`, and I + // don't understand how this callback is different from `raw_verify` above. + let callback = ctx + .ex_data(ssl_idx) + .expect("BUG: store context missing ssl") + .ex_data(Ssl::cached_ex_index::>()) + .expect("BUG: ssl verify callback missing") + .clone(); + + callback(preverify_ok != 0, ctx) as c_int +} + +pub(super) unsafe extern "C" fn raw_sni( + ssl: *mut ffi::SSL, + al: *mut c_int, + arg: *mut c_void, +) -> c_int +where + F: Fn(&mut SslRef, &mut SslAlert) -> Result<(), SniError> + 'static + Sync + Send, +{ + // SAFETY: boring provides valid inputs. + let ssl = unsafe { SslRef::from_ptr_mut(ssl) }; + let al = unsafe { &mut *al }; + + // SAFETY: We can make `callback` outlive `ssl` because it is a callback + // stored in the original context with which `ssl` was built. That + // original context is always stored as the session context in + // `Ssl::new` so it is always guaranteed to outlive the lifetime of + // this function's scope. + let callback = unsafe { &*(arg as *const F) }; + + let mut alert = SslAlert(*al); + + let r = callback(ssl, &mut alert); + + *al = alert.0; + + match r { + Ok(()) => ffi::SSL_TLSEXT_ERR_OK, + Err(e) => e.0, + } +} + +pub(super) unsafe extern "C" fn raw_alpn_select( + ssl: *mut ffi::SSL, + out: *mut *const c_uchar, + outlen: *mut c_uchar, + inbuf: *const c_uchar, + inlen: c_uint, + _arg: *mut c_void, +) -> c_int +where + F: for<'a> Fn(&mut SslRef, &'a [u8]) -> Result<&'a [u8], AlpnError> + 'static + Sync + Send, +{ + // SAFETY: boring provides valid inputs. + let ssl = unsafe { SslRef::from_ptr_mut(ssl) }; + let protos = unsafe { slice::from_raw_parts(inbuf, inlen as usize) }; + let out = unsafe { &mut *out }; + let outlen = unsafe { &mut *outlen }; + + let ssl_context = ssl.ssl_context().to_owned(); + let callback = ssl_context + .ex_data(SslContext::cached_ex_index::()) + .expect("BUG: alpn callback missing"); + + match callback(ssl, protos) { + Ok(proto) => { + *out = proto.as_ptr() as *const c_uchar; + *outlen = proto.len() as c_uchar; + + ffi::SSL_TLSEXT_ERR_OK + } + Err(e) => e.0, + } +} + +pub(super) unsafe extern "C" fn raw_select_cert( + client_hello: *const ffi::SSL_CLIENT_HELLO, +) -> ffi::ssl_select_cert_result_t +where + F: Fn(ClientHello<'_>) -> Result<(), SelectCertError> + Sync + Send + 'static, +{ + // SAFETY: boring provides valid inputs. + let client_hello = ClientHello(unsafe { &*client_hello }); + + let ssl_context = client_hello.ssl().ssl_context().to_owned(); + let callback = ssl_context + .ex_data(SslContext::cached_ex_index::()) + .expect("BUG: select cert callback missing"); + + match callback(client_hello) { + Ok(()) => ffi::ssl_select_cert_result_t::ssl_select_cert_success, + Err(e) => e.0, + } +} + +pub(super) unsafe extern "C" fn raw_tlsext_status(ssl: *mut ffi::SSL, _: *mut c_void) -> c_int +where + F: Fn(&mut SslRef) -> Result + 'static + Sync + Send, +{ + // SAFETY: boring provides valid inputs. + let ssl = unsafe { SslRef::from_ptr_mut(ssl) }; + + let ssl_context = ssl.ssl_context().to_owned(); + let callback = ssl_context + .ex_data(SslContext::cached_ex_index::()) + .expect("BUG: ocsp callback missing"); + + let ret = callback(ssl); + + if ssl.is_server() { + match ret { + Ok(true) => ffi::SSL_TLSEXT_ERR_OK, + Ok(false) => ffi::SSL_TLSEXT_ERR_NOACK, + Err(e) => { + e.put(); + ffi::SSL_TLSEXT_ERR_ALERT_FATAL + } + } + } else { + match ret { + Ok(true) => 1, + Ok(false) => 0, + Err(e) => { + e.put(); + -1 + } + } + } +} + +pub(super) unsafe extern "C" fn raw_new_session( + ssl: *mut ffi::SSL, + session: *mut ffi::SSL_SESSION, +) -> c_int +where + F: Fn(&mut SslRef, SslSession) + 'static + Sync + Send, +{ + // SAFETY: boring provides valid inputs. + let ssl = unsafe { SslRef::from_ptr_mut(ssl) }; + let session = unsafe { SslSession::from_ptr(session) }; + + let callback = ssl + .ex_data(*SESSION_CTX_INDEX) + .expect("BUG: session context missing") + .ex_data(SslContext::cached_ex_index::()) + .expect("BUG: new session callback missing"); + + // SAFETY: We can make `callback` outlive `ssl` because it is a callback + // stored in the session context set in `Ssl::new` so it is always + // guaranteed to outlive the lifetime of this function's scope. + let callback = unsafe { &*(callback as *const F) }; + + callback(ssl, session); + + // the return code doesn't indicate error vs success, but whether or not we consumed the session + 1 +} + +pub(super) unsafe extern "C" fn raw_remove_session( + ctx: *mut ffi::SSL_CTX, + session: *mut ffi::SSL_SESSION, +) where + F: Fn(&SslContextRef, &SslSessionRef) + 'static + Sync + Send, +{ + // SAFETY: boring provides valid inputs. + let ctx = unsafe { SslContextRef::from_ptr(ctx) }; + let session = unsafe { SslSessionRef::from_ptr(session) }; + + let callback = ctx + .ex_data(SslContext::cached_ex_index::()) + .expect("BUG: remove session callback missing"); + + callback(ctx, session) +} + +type DataPtr = *const c_uchar; + +pub(super) unsafe extern "C" fn raw_get_session( + ssl: *mut ffi::SSL, + data: DataPtr, + len: c_int, + copy: *mut c_int, +) -> *mut ffi::SSL_SESSION +where + F: Fn(&mut SslRef, &[u8]) -> Result, GetSessionPendingError> + + 'static + + Sync + + Send, +{ + // SAFETY: boring provides valid inputs. + let ssl = unsafe { SslRef::from_ptr_mut(ssl) }; + let data = unsafe { slice::from_raw_parts(data, len as usize) }; + let copy = unsafe { &mut *copy }; + + let callback = ssl + .ex_data(*SESSION_CTX_INDEX) + .expect("BUG: session context missing") + .ex_data(SslContext::cached_ex_index::()) + .expect("BUG: get session callback missing"); + + // SAFETY: We can make `callback` outlive `ssl` because it is a callback + // stored in the session context set in `Ssl::new` so it is always + // guaranteed to outlive the lifetime of this function's scope. + let callback = unsafe { &*(callback as *const F) }; + + match callback(ssl, data) { + Ok(Some(session)) => { + let p = session.into_ptr(); + + *copy = 0; + + p + } + Ok(None) => ptr::null_mut(), + Err(GetSessionPendingError) => unsafe { ffi::SSL_magic_pending_session_ptr() }, + } +} + +pub(super) unsafe extern "C" fn raw_keylog(ssl: *const ffi::SSL, line: *const c_char) +where + F: Fn(&SslRef, &str) + 'static + Sync + Send, +{ + // SAFETY: boring provides valid inputs. + let ssl = unsafe { SslRef::from_ptr(ssl as *mut _) }; + let line = unsafe { str::from_utf8_unchecked(CStr::from_ptr(line).to_bytes()) }; + + let callback = ssl + .ssl_context() + .ex_data(SslContext::cached_ex_index::()) + .expect("BUG: get session callback missing"); + + callback(ssl, line); +} + +pub(super) unsafe extern "C" fn raw_sign( + ssl: *mut ffi::SSL, + out: *mut u8, + out_len: *mut usize, + max_out: usize, + signature_algorithm: u16, + in_: *const u8, + in_len: usize, +) -> ffi::ssl_private_key_result_t +where + M: PrivateKeyMethod, +{ + // SAFETY: boring provides valid inputs. + let input = unsafe { slice::from_raw_parts(in_, in_len) }; + + let signature_algorithm = SslSignatureAlgorithm(signature_algorithm); + + let callback = |method: &M, ssl: &mut _, output: &mut _| { + method.sign(ssl, input, signature_algorithm, output) + }; + + // SAFETY: boring provides valid inputs. + unsafe { raw_private_key_callback(ssl, out, out_len, max_out, callback) } +} + +pub(super) unsafe extern "C" fn raw_decrypt( + ssl: *mut ffi::SSL, + out: *mut u8, + out_len: *mut usize, + max_out: usize, + in_: *const u8, + in_len: usize, +) -> ffi::ssl_private_key_result_t +where + M: PrivateKeyMethod, +{ + // SAFETY: boring provides valid inputs. + let input = unsafe { slice::from_raw_parts(in_, in_len) }; + + let callback = |method: &M, ssl: &mut _, output: &mut _| method.decrypt(ssl, input, output); + + // SAFETY: boring provides valid inputs. + unsafe { raw_private_key_callback(ssl, out, out_len, max_out, callback) } +} + +pub(super) unsafe extern "C" fn raw_complete( + ssl: *mut ffi::SSL, + out: *mut u8, + out_len: *mut usize, + max_out: usize, +) -> ffi::ssl_private_key_result_t +where + M: PrivateKeyMethod, +{ + // SAFETY: boring provides valid inputs. + unsafe { raw_private_key_callback::(ssl, out, out_len, max_out, M::complete) } +} + +unsafe fn raw_private_key_callback( + ssl: *mut ffi::SSL, + out: *mut u8, + out_len: *mut usize, + max_out: usize, + callback: impl FnOnce(&M, &mut SslRef, &mut [u8]) -> Result, +) -> ffi::ssl_private_key_result_t +where + M: PrivateKeyMethod, +{ + // SAFETY: boring provides valid inputs. + let ssl = unsafe { SslRef::from_ptr_mut(ssl) }; + let output = unsafe { slice::from_raw_parts_mut(out, max_out) }; + let out_len = unsafe { &mut *out_len }; + + let ssl_context = ssl.ssl_context().to_owned(); + let method = ssl_context + .ex_data(SslContext::cached_ex_index::()) + .expect("BUG: private key method missing"); + + match callback(method, ssl, output) { + Ok(written) => { + assert!(written <= max_out); + + *out_len = written; + + ffi::ssl_private_key_result_t::ssl_private_key_success + } + Err(err) => err.0, + } +} diff --git a/crates/boring/src/ssl/connector.rs b/crates/boring/src/ssl/connector.rs new file mode 100644 index 000000000..e910a324b --- /dev/null +++ b/crates/boring/src/ssl/connector.rs @@ -0,0 +1,446 @@ +use std::io::{Read, Write}; +use std::ops::{Deref, DerefMut}; + +use crate::dh::Dh; +use crate::error::ErrorStack; +use crate::ssl::{ + HandshakeError, Ssl, SslContext, SslContextBuilder, SslContextRef, SslMethod, SslMode, + SslOptions, SslRef, SslStream, SslVerifyMode, +}; +use crate::version; +use std::net::IpAddr; + +use super::MidHandshakeSslStream; + +const FFDHE_2048: &str = " +-----BEGIN DH PARAMETERS----- +MIIBCAKCAQEA//////////+t+FRYortKmq/cViAnPTzx2LnFg84tNpWp4TZBFGQz ++8yTnc4kmz75fS/jY2MMddj2gbICrsRhetPfHtXV/WVhJDP1H18GbtCFY2VVPe0a +87VXE15/V8k1mE8McODmi3fipona8+/och3xWKE2rec1MKzKT0g6eXq8CrGCsyT7 +YdEIqUuyyOP7uWrat2DX9GgdT0Kj3jlN9K5W7edjcrsZCwenyO4KbXCeAvzhzffi +7MA0BM0oNC9hkXL+nOmFg/+OTxIy7vKBg8P+OxtMb61zO7X8vC7CIAXFjvGDfRaD +ssbzSibBsu/6iGtCOGEoXJf//////////wIBAg== +-----END DH PARAMETERS----- +"; + +enum ContextType { + WithMethod(SslMethod), + #[cfg(feature = "rpk")] + Rpk, +} + +#[allow(clippy::inconsistent_digit_grouping)] +fn ctx(ty: ContextType) -> Result { + let mut ctx = match ty { + ContextType::WithMethod(method) => SslContextBuilder::new(method), + #[cfg(feature = "rpk")] + ContextType::Rpk => SslContextBuilder::new_rpk(), + }?; + + let mut opts = SslOptions::ALL + | SslOptions::NO_COMPRESSION + | SslOptions::NO_SSLV2 + | SslOptions::NO_SSLV3 + | SslOptions::SINGLE_DH_USE + | SslOptions::SINGLE_ECDH_USE; + opts &= !SslOptions::DONT_INSERT_EMPTY_FRAGMENTS; + + ctx.set_options(opts); + + let mut mode = + SslMode::AUTO_RETRY | SslMode::ACCEPT_MOVING_WRITE_BUFFER | SslMode::ENABLE_PARTIAL_WRITE; + + // This is quite a useful optimization for saving memory, but historically + // caused CVEs in OpenSSL pre-1.0.1h, according to + // https://bugs.python.org/issue25672 + if version::number() >= 0x1000_1080 { + mode |= SslMode::RELEASE_BUFFERS; + } + + ctx.set_mode(mode); + + Ok(ctx) +} + +/// A type which wraps client-side streams in a TLS session. +/// +/// OpenSSL's default configuration is highly insecure. This connector manages the OpenSSL +/// structures, configuring cipher suites, session options, hostname verification, and more. +/// +/// OpenSSL's built in hostname verification is used when linking against OpenSSL 1.0.2 or 1.1.0, +/// and a custom implementation is used when linking against OpenSSL 1.0.1. +#[derive(Clone, Debug)] +pub struct SslConnector(SslContext); + +impl SslConnector { + /// Creates a new builder for TLS connections. + /// + /// The default configuration is subject to change, and is currently derived from Python. + pub fn builder(method: SslMethod) -> Result { + let mut ctx = ctx(ContextType::WithMethod(method))?; + ctx.set_default_verify_paths()?; + ctx.set_cipher_list( + "DEFAULT:!aNULL:!eNULL:!MD5:!3DES:!DES:!RC4:!IDEA:!SEED:!aDSS:!SRP:!PSK", + )?; + setup_verify(&mut ctx); + + Ok(SslConnectorBuilder(ctx)) + } + + /// Creates a new builder for TLS connections with raw public key. + #[cfg(feature = "rpk")] + pub fn rpk_builder() -> Result { + let mut ctx = ctx(ContextType::Rpk)?; + ctx.set_cipher_list( + "DEFAULT:!aNULL:!eNULL:!MD5:!3DES:!DES:!RC4:!IDEA:!SEED:!aDSS:!SRP:!PSK", + )?; + + Ok(SslConnectorBuilder(ctx)) + } + + /// Initiates a client-side TLS session on a stream. + /// + /// The domain is used for SNI and hostname verification. + pub fn setup_connect( + &self, + domain: &str, + stream: S, + ) -> Result, ErrorStack> + where + S: Read + Write, + { + self.configure()?.setup_connect(domain, stream) + } + + /// Attempts a client-side TLS session on a stream. + /// + /// The domain is used for SNI (if it is not an IP address) and hostname verification if enabled. + /// + /// This is a convenience method which combines [`Self::setup_connect`] and + /// [`MidHandshakeSslStream::handshake`]. + pub fn connect(&self, domain: &str, stream: S) -> Result, HandshakeError> + where + S: Read + Write, + { + self.setup_connect(domain, stream) + .map_err(HandshakeError::SetupFailure)? + .handshake() + } + + /// Returns a structure allowing for configuration of a single TLS session before connection. + pub fn configure(&self) -> Result { + Ssl::new(&self.0).map(|ssl| ConnectConfiguration { + ssl, + sni: true, + verify_hostname: true, + }) + } + + /// Consumes the `SslConnector`, returning the inner raw `SslContext`. + pub fn into_context(self) -> SslContext { + self.0 + } + + /// Returns a shared reference to the inner raw `SslContext`. + pub fn context(&self) -> &SslContextRef { + &self.0 + } +} + +/// A builder for `SslConnector`s. +pub struct SslConnectorBuilder(SslContextBuilder); + +impl SslConnectorBuilder { + /// Consumes the builder, returning an `SslConnector`. + pub fn build(self) -> SslConnector { + SslConnector(self.0.build()) + } +} + +impl Deref for SslConnectorBuilder { + type Target = SslContextBuilder; + + fn deref(&self) -> &SslContextBuilder { + &self.0 + } +} + +impl DerefMut for SslConnectorBuilder { + fn deref_mut(&mut self) -> &mut SslContextBuilder { + &mut self.0 + } +} + +/// A type which allows for configuration of a client-side TLS session before connection. +pub struct ConnectConfiguration { + ssl: Ssl, + sni: bool, + verify_hostname: bool, +} + +impl ConnectConfiguration { + /// A builder-style version of `set_use_server_name_indication`. + pub fn use_server_name_indication(mut self, use_sni: bool) -> ConnectConfiguration { + self.set_use_server_name_indication(use_sni); + self + } + + /// Configures the use of Server Name Indication (SNI) when connecting. + /// + /// Defaults to `true`. + pub fn set_use_server_name_indication(&mut self, use_sni: bool) { + self.sni = use_sni; + } + + /// A builder-style version of `set_verify_hostname`. + pub fn verify_hostname(mut self, verify_hostname: bool) -> ConnectConfiguration { + self.set_verify_hostname(verify_hostname); + self + } + + /// Configures the use of hostname verification when connecting. + /// + /// Defaults to `true`. + /// + /// # Warning + /// + /// You should think very carefully before you use this method. If hostname verification is not + /// used, *any* valid certificate for *any* site will be trusted for use from any other. This + /// introduces a significant vulnerability to man-in-the-middle attacks. + pub fn set_verify_hostname(&mut self, verify_hostname: bool) { + self.verify_hostname = verify_hostname; + } + + /// Returns an [`Ssl`] configured to connect to the provided domain. + /// + /// The domain is used for SNI (if it is not an IP address) and hostname verification if enabled. + pub fn into_ssl(mut self, domain: &str) -> Result { + if self.sni && domain.parse::().is_err() { + self.ssl.set_hostname(domain)?; + } + + #[cfg(feature = "rpk")] + let verify_hostname = !self.ssl.ssl_context().is_rpk() && self.verify_hostname; + + #[cfg(not(feature = "rpk"))] + let verify_hostname = self.verify_hostname; + + if verify_hostname { + setup_verify_hostname(&mut self.ssl, domain)?; + } + + Ok(self.ssl) + } + + /// Initiates a client-side TLS session on a stream. + /// + /// The domain is used for SNI (if it is not an IP address) and hostname verification if enabled. + /// + /// This is a convenience method which combines [`Self::into_ssl`] and + /// [`Ssl::setup_connect`]. + pub fn setup_connect( + self, + domain: &str, + stream: S, + ) -> Result, ErrorStack> + where + S: Read + Write, + { + Ok(self.into_ssl(domain)?.setup_connect(stream)) + } + + /// Attempts a client-side TLS session on a stream. + /// + /// The domain is used for SNI (if it is not an IP address) and hostname verification if enabled. + /// + /// This is a convenience method which combines [`Self::setup_connect`] and + /// [`MidHandshakeSslStream::handshake`]. + pub fn connect(self, domain: &str, stream: S) -> Result, HandshakeError> + where + S: Read + Write, + { + self.setup_connect(domain, stream) + .map_err(HandshakeError::SetupFailure)? + .handshake() + } +} + +impl Deref for ConnectConfiguration { + type Target = SslRef; + + fn deref(&self) -> &SslRef { + &self.ssl + } +} + +impl DerefMut for ConnectConfiguration { + fn deref_mut(&mut self) -> &mut SslRef { + &mut self.ssl + } +} + +/// A type which wraps server-side streams in a TLS session. +/// +/// OpenSSL's default configuration is highly insecure. This connector manages the OpenSSL +/// structures, configuring cipher suites, session options, and more. +#[derive(Clone)] +pub struct SslAcceptor(SslContext); + +impl SslAcceptor { + /// Creates a new builder configured to connect to clients that support Raw Public Keys. + #[cfg(feature = "rpk")] + pub fn rpk() -> Result { + let mut ctx = ctx(ContextType::Rpk)?; + ctx.set_options(SslOptions::NO_TLSV1 | SslOptions::NO_TLSV1_1); + let dh = Dh::params_from_pem(FFDHE_2048.as_bytes())?; + ctx.set_tmp_dh(&dh)?; + ctx.set_cipher_list( + "ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:\ + ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:\ + DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384" + )?; + Ok(SslAcceptorBuilder(ctx)) + } + + /// Creates a new builder configured to connect to non-legacy clients. This should generally be + /// considered a reasonable default choice. + /// + /// This corresponds to the intermediate configuration of version 5 of Mozilla's server side TLS + /// recommendations. See its [documentation][docs] for more details on specifics. + /// + /// [docs]: https://wiki.mozilla.org/Security/Server_Side_TLS + pub fn mozilla_intermediate_v5(method: SslMethod) -> Result { + let mut ctx = ctx(ContextType::WithMethod(method))?; + ctx.set_options(SslOptions::NO_TLSV1 | SslOptions::NO_TLSV1_1); + let dh = Dh::params_from_pem(FFDHE_2048.as_bytes())?; + ctx.set_tmp_dh(&dh)?; + ctx.set_cipher_list( + "ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:\ + ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:\ + DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384" + )?; + Ok(SslAcceptorBuilder(ctx)) + } + + /// Creates a new builder configured to connect to non-legacy clients. This should generally be + /// considered a reasonable default choice. + /// + /// This corresponds to the intermediate configuration of version 4 of Mozilla's server side TLS + /// recommendations. See its [documentation][docs] for more details on specifics. + /// + /// [docs]: https://wiki.mozilla.org/Security/Server_Side_TLS + // FIXME remove in next major version + pub fn mozilla_intermediate(method: SslMethod) -> Result { + let mut ctx = ctx(ContextType::WithMethod(method))?; + ctx.set_options(SslOptions::CIPHER_SERVER_PREFERENCE); + ctx.set_options(SslOptions::NO_TLSV1_3); + let dh = Dh::params_from_pem(FFDHE_2048.as_bytes())?; + ctx.set_tmp_dh(&dh)?; + ctx.set_cipher_list( + "ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:\ + ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:\ + DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256:\ + ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES256-SHA384:\ + ECDHE-ECDSA-AES256-SHA:ECDHE-RSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:\ + DHE-RSA-AES256-SHA256:DHE-RSA-AES256-SHA:ECDHE-ECDSA-DES-CBC3-SHA:ECDHE-RSA-DES-CBC3-SHA:\ + EDH-RSA-DES-CBC3-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:\ + AES256-SHA:DES-CBC3-SHA:!DSS", + )?; + Ok(SslAcceptorBuilder(ctx)) + } + + /// Creates a new builder configured to connect to modern clients. + /// + /// This corresponds to the modern configuration of version 4 of Mozilla's server side TLS recommendations. + /// See its [documentation][docs] for more details on specifics. + /// + /// [docs]: https://wiki.mozilla.org/Security/Server_Side_TLS + // FIXME remove in next major version + pub fn mozilla_modern(method: SslMethod) -> Result { + let mut ctx = ctx(ContextType::WithMethod(method))?; + ctx.set_options( + SslOptions::CIPHER_SERVER_PREFERENCE | SslOptions::NO_TLSV1 | SslOptions::NO_TLSV1_1, + ); + ctx.set_options(SslOptions::NO_TLSV1_3); + ctx.set_cipher_list( + "ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:\ + ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:\ + ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256", + )?; + Ok(SslAcceptorBuilder(ctx)) + } + + /// Initiates a server-side TLS handshake on a stream. + /// + /// See [`Ssl::setup_accept`] for more details. + pub fn setup_accept(&self, stream: S) -> Result, ErrorStack> + where + S: Read + Write, + { + let ssl = Ssl::new(&self.0)?; + + Ok(ssl.setup_accept(stream)) + } + + /// Attempts a server-side TLS handshake on a stream. + /// + /// This is a convenience method which combines [`Self::setup_accept`] and + /// [`MidHandshakeSslStream::handshake`]. + pub fn accept(&self, stream: S) -> Result, HandshakeError> + where + S: Read + Write, + { + self.setup_accept(stream) + .map_err(HandshakeError::SetupFailure)? + .handshake() + } + + /// Consumes the `SslAcceptor`, returning the inner raw `SslContext`. + pub fn into_context(self) -> SslContext { + self.0 + } + + /// Returns a shared reference to the inner raw `SslContext`. + pub fn context(&self) -> &SslContextRef { + &self.0 + } +} + +/// A builder for `SslAcceptor`s. +pub struct SslAcceptorBuilder(SslContextBuilder); + +impl SslAcceptorBuilder { + /// Consumes the builder, returning a `SslAcceptor`. + pub fn build(self) -> SslAcceptor { + SslAcceptor(self.0.build()) + } +} + +impl Deref for SslAcceptorBuilder { + type Target = SslContextBuilder; + + fn deref(&self) -> &SslContextBuilder { + &self.0 + } +} + +impl DerefMut for SslAcceptorBuilder { + fn deref_mut(&mut self) -> &mut SslContextBuilder { + &mut self.0 + } +} + +fn setup_verify(ctx: &mut SslContextBuilder) { + ctx.set_verify(SslVerifyMode::PEER); +} + +fn setup_verify_hostname(ssl: &mut SslRef, domain: &str) -> Result<(), ErrorStack> { + use crate::x509::verify::X509CheckFlags; + + let param = ssl.param_mut(); + param.set_hostflags(X509CheckFlags::NO_PARTIAL_WILDCARDS); + match domain.parse() { + Ok(ip) => param.set_ip(ip), + Err(_) => param.set_host(domain), + } +} diff --git a/crates/boring/src/ssl/error.rs b/crates/boring/src/ssl/error.rs new file mode 100644 index 000000000..0f75b8184 --- /dev/null +++ b/crates/boring/src/ssl/error.rs @@ -0,0 +1,213 @@ +use crate::ffi; +use libc::c_int; +use std::error; +use std::error::Error as StdError; +use std::fmt; +use std::io; + +use crate::error::ErrorStack; +use crate::ssl::MidHandshakeSslStream; + +/// An error code returned from SSL functions. +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +pub struct ErrorCode(c_int); + +impl ErrorCode { + /// The SSL session has been closed. + pub const ZERO_RETURN: ErrorCode = ErrorCode(ffi::SSL_ERROR_ZERO_RETURN); + + /// An attempt to read data from the underlying socket returned `WouldBlock`. + /// + /// Wait for read readiness and retry the operation. + pub const WANT_READ: ErrorCode = ErrorCode(ffi::SSL_ERROR_WANT_READ); + + /// An attempt to write data to the underlying socket returned `WouldBlock`. + /// + /// Wait for write readiness and retry the operation. + pub const WANT_WRITE: ErrorCode = ErrorCode(ffi::SSL_ERROR_WANT_WRITE); + + pub const WANT_X509_LOOKUP: ErrorCode = ErrorCode(ffi::SSL_ERROR_WANT_X509_LOOKUP); + + pub const PENDING_SESSION: ErrorCode = ErrorCode(ffi::SSL_ERROR_PENDING_SESSION); + + pub const PENDING_CERTIFICATE: ErrorCode = ErrorCode(ffi::SSL_ERROR_PENDING_CERTIFICATE); + + pub const WANT_PRIVATE_KEY_OPERATION: ErrorCode = + ErrorCode(ffi::SSL_ERROR_WANT_PRIVATE_KEY_OPERATION); + + pub const PENDING_TICKET: ErrorCode = ErrorCode(ffi::SSL_ERROR_PENDING_TICKET); + + /// A non-recoverable IO error occurred. + pub const SYSCALL: ErrorCode = ErrorCode(ffi::SSL_ERROR_SYSCALL); + + /// An error occurred in the SSL library. + pub const SSL: ErrorCode = ErrorCode(ffi::SSL_ERROR_SSL); + + pub fn from_raw(raw: c_int) -> ErrorCode { + ErrorCode(raw) + } + + #[allow(clippy::trivially_copy_pass_by_ref)] + pub fn as_raw(&self) -> c_int { + self.0 + } +} + +#[derive(Debug)] +pub(crate) enum InnerError { + Io(io::Error), + Ssl(ErrorStack), +} + +/// An SSL error. +#[derive(Debug)] +pub struct Error { + pub(crate) code: ErrorCode, + pub(crate) cause: Option, +} + +impl Error { + pub fn code(&self) -> ErrorCode { + self.code + } + + pub fn io_error(&self) -> Option<&io::Error> { + match self.cause { + Some(InnerError::Io(ref e)) => Some(e), + _ => None, + } + } + + pub fn into_io_error(self) -> Result { + match self.cause { + Some(InnerError::Io(e)) => Ok(e), + _ => Err(self), + } + } + + pub fn ssl_error(&self) -> Option<&ErrorStack> { + match self.cause { + Some(InnerError::Ssl(ref e)) => Some(e), + _ => None, + } + } + + pub fn would_block(&self) -> bool { + matches!( + self.code, + ErrorCode::WANT_READ + | ErrorCode::WANT_WRITE + | ErrorCode::WANT_X509_LOOKUP + | ErrorCode::PENDING_SESSION + | ErrorCode::PENDING_CERTIFICATE + | ErrorCode::WANT_PRIVATE_KEY_OPERATION + | ErrorCode::PENDING_TICKET + ) + } +} + +impl From for Error { + fn from(e: ErrorStack) -> Error { + Error { + code: ErrorCode::SSL, + cause: Some(InnerError::Ssl(e)), + } + } +} + +impl fmt::Display for Error { + fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { + match self.code { + ErrorCode::ZERO_RETURN => fmt.write_str("the SSL session has been shut down"), + ErrorCode::WANT_READ => match self.io_error() { + Some(_) => fmt.write_str("a nonblocking read call would have blocked"), + None => fmt.write_str("the operation should be retried"), + }, + ErrorCode::WANT_WRITE => match self.io_error() { + Some(_) => fmt.write_str("a nonblocking write call would have blocked"), + None => fmt.write_str("the operation should be retried"), + }, + ErrorCode::SYSCALL => match self.io_error() { + Some(err) => write!(fmt, "{}", err), + None => fmt.write_str("unexpected EOF"), + }, + ErrorCode::SSL => match self.ssl_error() { + Some(e) => write!(fmt, "{}", e), + None => fmt.write_str("unknown BoringSSL error"), + }, + ErrorCode(code) => write!(fmt, "unknown error code {}", code), + } + } +} + +impl error::Error for Error { + fn source(&self) -> Option<&(dyn error::Error + 'static)> { + match self.cause { + Some(InnerError::Io(ref e)) => Some(e), + Some(InnerError::Ssl(ref e)) => Some(e), + None => None, + } + } +} + +/// An error or intermediate state after a TLS handshake attempt. +// FIXME overhaul +#[derive(Debug)] +pub enum HandshakeError { + /// Setup failed. + SetupFailure(ErrorStack), + /// The handshake failed. + Failure(MidHandshakeSslStream), + /// The handshake encountered a `WouldBlock` error midway through. + /// + /// This error will never be returned for blocking streams. + WouldBlock(MidHandshakeSslStream), +} + +impl StdError for HandshakeError { + fn source(&self) -> Option<&(dyn StdError + 'static)> { + match *self { + HandshakeError::SetupFailure(ref e) => Some(e), + HandshakeError::Failure(ref s) | HandshakeError::WouldBlock(ref s) => Some(s.error()), + } + } +} + +impl fmt::Display for HandshakeError { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match *self { + HandshakeError::SetupFailure(ref e) => { + write!(f, "TLS stream setup failed {}", e) + } + HandshakeError::Failure(ref s) => fmt_mid_handshake_error(s, f, "TLS handshake failed"), + HandshakeError::WouldBlock(ref s) => { + fmt_mid_handshake_error(s, f, "TLS handshake interrupted") + } + } + } +} + +fn fmt_mid_handshake_error( + s: &MidHandshakeSslStream, + f: &mut fmt::Formatter, + prefix: &str, +) -> fmt::Result { + #[cfg(feature = "rpk")] + if s.ssl().ssl_context().is_rpk() { + write!(f, "{}", prefix)?; + return write!(f, " {}", s.error()); + } + + match s.ssl().verify_result() { + Ok(()) => write!(f, "{}", prefix)?, + Err(verify) => write!(f, "{}: cert verification failed - {}", prefix, verify)?, + } + + write!(f, " {}", s.error()) +} + +impl From for HandshakeError { + fn from(e: ErrorStack) -> HandshakeError { + HandshakeError::SetupFailure(e) + } +} diff --git a/crates/boring/src/ssl/mod.rs b/crates/boring/src/ssl/mod.rs new file mode 100644 index 000000000..1420ee327 --- /dev/null +++ b/crates/boring/src/ssl/mod.rs @@ -0,0 +1,3954 @@ +//! SSL/TLS support. +//! +//! `SslConnector` and `SslAcceptor` should be used in most cases - they handle +//! configuration of the OpenSSL primitives for you. +//! +//! # Examples +//! +//! To connect as a client to a remote server: +//! +//! ```no_run +//! use boring::ssl::{SslMethod, SslConnector}; +//! use std::io::{Read, Write}; +//! use std::net::TcpStream; +//! +//! let connector = SslConnector::builder(SslMethod::tls()).unwrap().build(); +//! +//! let stream = TcpStream::connect("google.com:443").unwrap(); +//! let mut stream = connector.connect("google.com", stream).unwrap(); +//! +//! stream.write_all(b"GET / HTTP/1.0\r\n\r\n").unwrap(); +//! let mut res = vec![]; +//! stream.read_to_end(&mut res).unwrap(); +//! println!("{}", String::from_utf8_lossy(&res)); +//! ``` +//! +//! To accept connections as a server from remote clients: +//! +//! ```no_run +//! use boring::ssl::{SslMethod, SslAcceptor, SslStream, SslFiletype}; +//! use std::net::{TcpListener, TcpStream}; +//! use std::sync::Arc; +//! use std::thread; +//! +//! +//! let mut acceptor = SslAcceptor::mozilla_intermediate(SslMethod::tls()).unwrap(); +//! acceptor.set_private_key_file("key.pem", SslFiletype::PEM).unwrap(); +//! acceptor.set_certificate_chain_file("certs.pem").unwrap(); +//! acceptor.check_private_key().unwrap(); +//! let acceptor = Arc::new(acceptor.build()); +//! +//! let listener = TcpListener::bind("0.0.0.0:8443").unwrap(); +//! +//! fn handle_client(stream: SslStream) { +//! // ... +//! } +//! +//! for stream in listener.incoming() { +//! match stream { +//! Ok(stream) => { +//! let acceptor = acceptor.clone(); +//! thread::spawn(move || { +//! let stream = acceptor.accept(stream).unwrap(); +//! handle_client(stream); +//! }); +//! } +//! Err(e) => { /* connection failed */ } +//! } +//! } +//! ``` +use crate::ffi; +use foreign_types::{ForeignType, ForeignTypeRef, Opaque}; +use libc::{c_char, c_int, c_long, c_uchar, c_uint, c_void}; +use once_cell::sync::Lazy; +use std::any::TypeId; +use std::cmp; +use std::collections::HashMap; +use std::convert::TryInto; +use std::ffi::{CStr, CString}; +use std::fmt; +use std::io; +use std::io::prelude::*; +use std::marker::PhantomData; +use std::mem::{self, ManuallyDrop}; +use std::ops::{Deref, DerefMut}; +use std::panic::resume_unwind; +use std::path::Path; +use std::ptr::{self, NonNull}; +use std::slice; +use std::str; +use std::sync::{Arc, Mutex}; + +use crate::dh::DhRef; +use crate::ec::EcKeyRef; +use crate::error::ErrorStack; +use crate::ex_data::Index; +use crate::nid::Nid; +use crate::pkey::{HasPrivate, PKeyRef, Params, Private}; +use crate::srtp::{SrtpProtectionProfile, SrtpProtectionProfileRef}; +use crate::ssl::bio::BioMethod; +use crate::ssl::callbacks::*; +use crate::ssl::error::InnerError; +use crate::stack::{Stack, StackRef}; +use crate::x509::store::{X509Store, X509StoreBuilderRef, X509StoreRef}; +use crate::x509::verify::X509VerifyParamRef; +use crate::x509::{ + X509Name, X509Ref, X509StoreContextRef, X509VerifyError, X509VerifyResult, X509, +}; +use crate::{cvt, cvt_0i, cvt_n, cvt_p, init}; + +pub use crate::ssl::connector::{ + ConnectConfiguration, SslAcceptor, SslAcceptorBuilder, SslConnector, SslConnectorBuilder, +}; +pub use crate::ssl::error::{Error, ErrorCode, HandshakeError}; + +mod bio; +mod callbacks; +mod connector; +mod error; +#[cfg(test)] +mod test; + +bitflags! { + /// Options controlling the behavior of an `SslContext`. + #[derive(Debug, PartialEq, Eq, Clone, Copy, PartialOrd, Ord, Hash)] + pub struct SslOptions: c_uint { + /// Disables a countermeasure against an SSLv3/TLSv1.0 vulnerability affecting CBC ciphers. + const DONT_INSERT_EMPTY_FRAGMENTS = ffi::SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS as _; + + /// A "reasonable default" set of options which enables compatibility flags. + const ALL = ffi::SSL_OP_ALL as _; + + /// Do not query the MTU. + /// + /// Only affects DTLS connections. + const NO_QUERY_MTU = ffi::SSL_OP_NO_QUERY_MTU as _; + + /// Disables the use of session tickets for session resumption. + const NO_TICKET = ffi::SSL_OP_NO_TICKET as _; + + /// Always start a new session when performing a renegotiation on the server side. + const NO_SESSION_RESUMPTION_ON_RENEGOTIATION = + ffi::SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION as _; + + /// Disables the use of TLS compression. + const NO_COMPRESSION = ffi::SSL_OP_NO_COMPRESSION as _; + + /// Allow legacy insecure renegotiation with servers or clients that do not support secure + /// renegotiation. + const ALLOW_UNSAFE_LEGACY_RENEGOTIATION = + ffi::SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION as _; + + /// Creates a new key for each session when using ECDHE. + const SINGLE_ECDH_USE = ffi::SSL_OP_SINGLE_ECDH_USE as _; + + /// Creates a new key for each session when using DHE. + const SINGLE_DH_USE = ffi::SSL_OP_SINGLE_DH_USE as _; + + /// Use the server's preferences rather than the client's when selecting a cipher. + /// + /// This has no effect on the client side. + const CIPHER_SERVER_PREFERENCE = ffi::SSL_OP_CIPHER_SERVER_PREFERENCE as _; + + /// Disables version rollback attach detection. + const TLS_ROLLBACK_BUG = ffi::SSL_OP_TLS_ROLLBACK_BUG as _; + + /// Disables the use of SSLv2. + const NO_SSLV2 = ffi::SSL_OP_NO_SSLv2 as _; + + /// Disables the use of SSLv3. + const NO_SSLV3 = ffi::SSL_OP_NO_SSLv3 as _; + + /// Disables the use of TLSv1.0. + const NO_TLSV1 = ffi::SSL_OP_NO_TLSv1 as _; + + /// Disables the use of TLSv1.1. + const NO_TLSV1_1 = ffi::SSL_OP_NO_TLSv1_1 as _; + + /// Disables the use of TLSv1.2. + const NO_TLSV1_2 = ffi::SSL_OP_NO_TLSv1_2 as _; + + /// Disables the use of TLSv1.3. + const NO_TLSV1_3 = ffi::SSL_OP_NO_TLSv1_3 as _; + + /// Disables the use of DTLSv1.0 + const NO_DTLSV1 = ffi::SSL_OP_NO_DTLSv1 as _; + + /// Disables the use of DTLSv1.2. + const NO_DTLSV1_2 = ffi::SSL_OP_NO_DTLSv1_2 as _; + + /// Disallow all renegotiation in TLSv1.2 and earlier. + const NO_RENEGOTIATION = ffi::SSL_OP_NO_RENEGOTIATION as _; + } +} + +bitflags! { + /// Options controlling the behavior of an `SslContext`. + #[derive(Debug, PartialEq, Eq, Clone, Copy, PartialOrd, Ord, Hash)] + pub struct SslMode: c_uint { + /// Enables "short writes". + /// + /// Normally, a write in OpenSSL will always write out all of the requested data, even if it + /// requires more than one TLS record or write to the underlying stream. This option will + /// cause a write to return after writing a single TLS record instead. + const ENABLE_PARTIAL_WRITE = ffi::SSL_MODE_ENABLE_PARTIAL_WRITE as _; + + /// Disables a check that the data buffer has not moved between calls when operating in a + /// nonblocking context. + const ACCEPT_MOVING_WRITE_BUFFER = ffi::SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER as _; + + /// Enables automatic retries after TLS session events such as renegotiations or heartbeats. + /// + /// By default, OpenSSL will return a `WantRead` error after a renegotiation or heartbeat. + /// This option will cause OpenSSL to automatically continue processing the requested + /// operation instead. + /// + /// Note that `SslStream::read` and `SslStream::write` will automatically retry regardless + /// of the state of this option. It only affects `SslStream::ssl_read` and + /// `SslStream::ssl_write`. + const AUTO_RETRY = ffi::SSL_MODE_AUTO_RETRY as _; + + /// Disables automatic chain building when verifying a peer's certificate. + /// + /// TLS peers are responsible for sending the entire certificate chain from the leaf to a + /// trusted root, but some will incorrectly not do so. OpenSSL will try to build the chain + /// out of certificates it knows of, and this option will disable that behavior. + const NO_AUTO_CHAIN = ffi::SSL_MODE_NO_AUTO_CHAIN as _; + + /// Release memory buffers when the session does not need them. + /// + /// This saves ~34 KiB of memory for idle streams. + const RELEASE_BUFFERS = ffi::SSL_MODE_RELEASE_BUFFERS as _; + + /// Sends the fake `TLS_FALLBACK_SCSV` cipher suite in the ClientHello message of a + /// handshake. + /// + /// This should only be enabled if a client has failed to connect to a server which + /// attempted to downgrade the protocol version of the session. + /// + /// Do not use this unless you know what you're doing! + const SEND_FALLBACK_SCSV = ffi::SSL_MODE_SEND_FALLBACK_SCSV as _; + } +} + +/// A type specifying the kind of protocol an `SslContext` will speak. +#[derive(Copy, Clone)] +pub struct SslMethod(*const ffi::SSL_METHOD); + +impl SslMethod { + /// Support all versions of the TLS protocol. + /// + /// This corresponds to `TLS_method` on OpenSSL 1.1.0 and `SSLv23_method` + /// on OpenSSL 1.0.x. + pub fn tls() -> SslMethod { + unsafe { SslMethod(TLS_method()) } + } + + /// Same as `tls`, but doesn't create X509 for certificates. + #[cfg(feature = "rpk")] + pub fn tls_with_buffer() -> SslMethod { + unsafe { SslMethod(ffi::TLS_with_buffers_method()) } + } + + /// Support all versions of the DTLS protocol. + /// + /// This corresponds to `DTLS_method` on OpenSSL 1.1.0 and `DTLSv1_method` + /// on OpenSSL 1.0.x. + pub fn dtls() -> SslMethod { + unsafe { SslMethod(DTLS_method()) } + } + + /// Support all versions of the TLS protocol, explicitly as a client. + /// + /// This corresponds to `TLS_client_method` on OpenSSL 1.1.0 and + /// `SSLv23_client_method` on OpenSSL 1.0.x. + pub fn tls_client() -> SslMethod { + unsafe { SslMethod(TLS_client_method()) } + } + + /// Support all versions of the TLS protocol, explicitly as a server. + /// + /// This corresponds to `TLS_server_method` on OpenSSL 1.1.0 and + /// `SSLv23_server_method` on OpenSSL 1.0.x. + pub fn tls_server() -> SslMethod { + unsafe { SslMethod(TLS_server_method()) } + } + + /// Constructs an `SslMethod` from a pointer to the underlying OpenSSL value. + /// + /// # Safety + /// + /// The caller must ensure the pointer is valid. + pub unsafe fn from_ptr(ptr: *const ffi::SSL_METHOD) -> SslMethod { + SslMethod(ptr) + } + + /// Returns a pointer to the underlying OpenSSL value. + #[allow(clippy::trivially_copy_pass_by_ref)] + pub fn as_ptr(&self) -> *const ffi::SSL_METHOD { + self.0 + } +} + +unsafe impl Sync for SslMethod {} +unsafe impl Send for SslMethod {} + +bitflags! { + /// Options controling the behavior of certificate verification. + #[derive(Debug, PartialEq, Eq, Clone, Copy, PartialOrd, Ord, Hash)] + pub struct SslVerifyMode: i32 { + /// Verifies that the peer's certificate is trusted. + /// + /// On the server side, this will cause OpenSSL to request a certificate from the client. + const PEER = ffi::SSL_VERIFY_PEER; + + /// Disables verification of the peer's certificate. + /// + /// On the server side, this will cause OpenSSL to not request a certificate from the + /// client. On the client side, the certificate will be checked for validity, but the + /// negotiation will continue regardless of the result of that check. + const NONE = ffi::SSL_VERIFY_NONE; + + /// On the server side, abort the handshake if the client did not send a certificate. + /// + /// This should be paired with `SSL_VERIFY_PEER`. It has no effect on the client side. + const FAIL_IF_NO_PEER_CERT = ffi::SSL_VERIFY_FAIL_IF_NO_PEER_CERT; + } +} + +bitflags! { + /// Options controlling the behavior of session caching. + #[derive(Debug, PartialEq, Eq, Clone, Copy, PartialOrd, Ord, Hash)] + pub struct SslSessionCacheMode: c_int { + /// No session caching for the client or server takes place. + const OFF = ffi::SSL_SESS_CACHE_OFF; + + /// Enable session caching on the client side. + /// + /// OpenSSL has no way of identifying the proper session to reuse automatically, so the + /// application is responsible for setting it explicitly via [`SslRef::set_session`]. + /// + /// [`SslRef::set_session`]: struct.SslRef.html#method.set_session + const CLIENT = ffi::SSL_SESS_CACHE_CLIENT; + + /// Enable session caching on the server side. + /// + /// This is the default mode. + const SERVER = ffi::SSL_SESS_CACHE_SERVER; + + /// Enable session caching on both the client and server side. + const BOTH = ffi::SSL_SESS_CACHE_BOTH; + + /// Disable automatic removal of expired sessions from the session cache. + const NO_AUTO_CLEAR = ffi::SSL_SESS_CACHE_NO_AUTO_CLEAR; + + /// Disable use of the internal session cache for session lookups. + const NO_INTERNAL_LOOKUP = ffi::SSL_SESS_CACHE_NO_INTERNAL_LOOKUP; + + /// Disable use of the internal session cache for session storage. + const NO_INTERNAL_STORE = ffi::SSL_SESS_CACHE_NO_INTERNAL_STORE; + + /// Disable use of the internal session cache for storage and lookup. + const NO_INTERNAL = ffi::SSL_SESS_CACHE_NO_INTERNAL; + } +} + +/// An identifier of the format of a certificate or key file. +#[derive(Copy, Clone)] +pub struct SslFiletype(c_int); + +impl SslFiletype { + /// The PEM format. + /// + /// This corresponds to `SSL_FILETYPE_PEM`. + pub const PEM: SslFiletype = SslFiletype(ffi::SSL_FILETYPE_PEM); + + /// The ASN1 format. + /// + /// This corresponds to `SSL_FILETYPE_ASN1`. + pub const ASN1: SslFiletype = SslFiletype(ffi::SSL_FILETYPE_ASN1); + + /// Constructs an `SslFiletype` from a raw OpenSSL value. + pub fn from_raw(raw: c_int) -> SslFiletype { + SslFiletype(raw) + } + + /// Returns the raw OpenSSL value represented by this type. + #[allow(clippy::trivially_copy_pass_by_ref)] + pub fn as_raw(&self) -> c_int { + self.0 + } +} + +/// An identifier of a certificate status type. +#[derive(Copy, Clone)] +pub struct StatusType(c_int); + +impl StatusType { + /// An OSCP status. + pub const OCSP: StatusType = StatusType(ffi::TLSEXT_STATUSTYPE_ocsp); + + /// Constructs a `StatusType` from a raw OpenSSL value. + pub fn from_raw(raw: c_int) -> StatusType { + StatusType(raw) + } + + /// Returns the raw OpenSSL value represented by this type. + #[allow(clippy::trivially_copy_pass_by_ref)] + pub fn as_raw(&self) -> c_int { + self.0 + } +} + +/// An identifier of a session name type. +#[derive(Copy, Clone)] +pub struct NameType(c_int); + +impl NameType { + /// A host name. + pub const HOST_NAME: NameType = NameType(ffi::TLSEXT_NAMETYPE_host_name); + + /// Constructs a `StatusType` from a raw OpenSSL value. + pub fn from_raw(raw: c_int) -> StatusType { + StatusType(raw) + } + + /// Returns the raw OpenSSL value represented by this type. + #[allow(clippy::trivially_copy_pass_by_ref)] + pub fn as_raw(&self) -> c_int { + self.0 + } +} + +static INDEXES: Lazy>> = Lazy::new(|| Mutex::new(HashMap::new())); +static SSL_INDEXES: Lazy>> = Lazy::new(|| Mutex::new(HashMap::new())); +static SESSION_CTX_INDEX: Lazy> = Lazy::new(|| Ssl::new_ex_index().unwrap()); +#[cfg(feature = "rpk")] +static RPK_FLAG_INDEX: Lazy> = + Lazy::new(|| SslContext::new_ex_index().unwrap()); + +unsafe extern "C" fn free_data_box( + _parent: *mut c_void, + ptr: *mut c_void, + _ad: *mut ffi::CRYPTO_EX_DATA, + _idx: c_int, + _argl: c_long, + _argp: *mut c_void, +) { + if !ptr.is_null() { + drop(Box::::from_raw(ptr as *mut T)); + } +} + +/// An error returned from the SNI callback. +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +pub struct SniError(c_int); + +impl SniError { + /// Abort the handshake with a fatal alert. + pub const ALERT_FATAL: SniError = SniError(ffi::SSL_TLSEXT_ERR_ALERT_FATAL); + + /// Send a warning alert to the client and continue the handshake. + pub const ALERT_WARNING: SniError = SniError(ffi::SSL_TLSEXT_ERR_ALERT_WARNING); + + pub const NOACK: SniError = SniError(ffi::SSL_TLSEXT_ERR_NOACK); +} + +/// An SSL/TLS alert. +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +pub struct SslAlert(c_int); + +impl SslAlert { + /// Alert 112 - `unrecognized_name`. + pub const UNRECOGNIZED_NAME: SslAlert = SslAlert(ffi::SSL_AD_UNRECOGNIZED_NAME); + pub const ILLEGAL_PARAMETER: SslAlert = SslAlert(ffi::SSL_AD_ILLEGAL_PARAMETER); + pub const DECODE_ERROR: SslAlert = SslAlert(ffi::SSL_AD_DECODE_ERROR); +} + +/// An error returned from an ALPN selection callback. +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +pub struct AlpnError(c_int); + +impl AlpnError { + /// Terminate the handshake with a fatal alert. + pub const ALERT_FATAL: AlpnError = AlpnError(ffi::SSL_TLSEXT_ERR_ALERT_FATAL); + + /// Do not select a protocol, but continue the handshake. + pub const NOACK: AlpnError = AlpnError(ffi::SSL_TLSEXT_ERR_NOACK); +} + +/// An error returned from a certificate selection callback. +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +pub struct SelectCertError(ffi::ssl_select_cert_result_t); + +impl SelectCertError { + /// A fatal error occured and the handshake should be terminated. + pub const ERROR: Self = Self(ffi::ssl_select_cert_result_t::ssl_select_cert_error); + + /// The operation could not be completed and should be retried later. + pub const RETRY: Self = Self(ffi::ssl_select_cert_result_t::ssl_select_cert_retry); +} + +/// Extension types, to be used with `ClientHello::get_extension`. +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +pub struct ExtensionType(u16); + +impl ExtensionType { + pub const SERVER_NAME: Self = Self(ffi::TLSEXT_TYPE_server_name as u16); + pub const STATUS_REQUEST: Self = Self(ffi::TLSEXT_TYPE_status_request as u16); + pub const EC_POINT_FORMATS: Self = Self(ffi::TLSEXT_TYPE_ec_point_formats as u16); + pub const SIGNATURE_ALGORITHMS: Self = Self(ffi::TLSEXT_TYPE_signature_algorithms as u16); + pub const SRTP: Self = Self(ffi::TLSEXT_TYPE_srtp as u16); + pub const APPLICATION_LAYER_PROTOCOL_NEGOTIATION: Self = + Self(ffi::TLSEXT_TYPE_application_layer_protocol_negotiation as u16); + pub const PADDING: Self = Self(ffi::TLSEXT_TYPE_padding as u16); + pub const EXTENDED_MASTER_SECRET: Self = Self(ffi::TLSEXT_TYPE_extended_master_secret as u16); + pub const QUIC_TRANSPORT_PARAMETERS_LEGACY: Self = + Self(ffi::TLSEXT_TYPE_quic_transport_parameters_legacy as u16); + pub const QUIC_TRANSPORT_PARAMETERS_STANDARD: Self = + Self(ffi::TLSEXT_TYPE_quic_transport_parameters_standard as u16); + pub const CERT_COMPRESSION: Self = Self(ffi::TLSEXT_TYPE_cert_compression as u16); + pub const SESSION_TICKET: Self = Self(ffi::TLSEXT_TYPE_session_ticket as u16); + pub const SUPPORTED_GROUPS: Self = Self(ffi::TLSEXT_TYPE_supported_groups as u16); + pub const PRE_SHARED_KEY: Self = Self(ffi::TLSEXT_TYPE_pre_shared_key as u16); + pub const EARLY_DATA: Self = Self(ffi::TLSEXT_TYPE_early_data as u16); + pub const SUPPORTED_VERSIONS: Self = Self(ffi::TLSEXT_TYPE_supported_versions as u16); + pub const COOKIE: Self = Self(ffi::TLSEXT_TYPE_cookie as u16); + pub const PSK_KEY_EXCHANGE_MODES: Self = Self(ffi::TLSEXT_TYPE_psk_key_exchange_modes as u16); + pub const CERTIFICATE_AUTHORITIES: Self = Self(ffi::TLSEXT_TYPE_certificate_authorities as u16); + pub const SIGNATURE_ALGORITHMS_CERT: Self = + Self(ffi::TLSEXT_TYPE_signature_algorithms_cert as u16); + pub const KEY_SHARE: Self = Self(ffi::TLSEXT_TYPE_key_share as u16); + pub const RENEGOTIATE: Self = Self(ffi::TLSEXT_TYPE_renegotiate as u16); + pub const DELEGATED_CREDENTIAL: Self = Self(ffi::TLSEXT_TYPE_delegated_credential as u16); + pub const APPLICATION_SETTINGS: Self = Self(ffi::TLSEXT_TYPE_application_settings as u16); + pub const ENCRYPTED_CLIENT_HELLO: Self = Self(ffi::TLSEXT_TYPE_encrypted_client_hello as u16); + pub const CERTIFICATE_TIMESTAMP: Self = Self(ffi::TLSEXT_TYPE_certificate_timestamp as u16); + pub const NEXT_PROTO_NEG: Self = Self(ffi::TLSEXT_TYPE_next_proto_neg as u16); + pub const CHANNEL_ID: Self = Self(ffi::TLSEXT_TYPE_channel_id as u16); +} + +impl From for ExtensionType { + fn from(value: u16) -> Self { + Self(value) + } +} + +/// An SSL/TLS protocol version. +#[derive(Copy, Clone, PartialEq, Eq)] +pub struct SslVersion(u16); + +impl SslVersion { + /// SSLv3 + pub const SSL3: SslVersion = SslVersion(ffi::SSL3_VERSION as _); + + /// TLSv1.0 + pub const TLS1: SslVersion = SslVersion(ffi::TLS1_VERSION as _); + + /// TLSv1.1 + pub const TLS1_1: SslVersion = SslVersion(ffi::TLS1_1_VERSION as _); + + /// TLSv1.2 + pub const TLS1_2: SslVersion = SslVersion(ffi::TLS1_2_VERSION as _); + + /// TLSv1.3 + pub const TLS1_3: SslVersion = SslVersion(ffi::TLS1_3_VERSION as _); +} + +impl fmt::Debug for SslVersion { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.write_str(match *self { + Self::SSL3 => "SSL3", + Self::TLS1 => "TLS1", + Self::TLS1_1 => "TLS1_1", + Self::TLS1_2 => "TLS1_2", + Self::TLS1_3 => "TLS1_3", + _ => return write!(f, "{:#06x}", self.0), + }) + } +} + +impl fmt::Display for SslVersion { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.write_str(match *self { + Self::SSL3 => "SSLv3", + Self::TLS1 => "TLSv1", + Self::TLS1_1 => "TLSv1.1", + Self::TLS1_2 => "TLSv1.2", + Self::TLS1_3 => "TLSv1.3", + _ => return write!(f, "unknown ({:#06x})", self.0), + }) + } +} + +/// A signature verification algorithm. +#[repr(transparent)] +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +pub struct SslSignatureAlgorithm(u16); + +impl SslSignatureAlgorithm { + pub const RSA_PKCS1_SHA1: SslSignatureAlgorithm = + SslSignatureAlgorithm(ffi::SSL_SIGN_RSA_PKCS1_SHA1 as _); + + pub const RSA_PKCS1_SHA256: SslSignatureAlgorithm = + SslSignatureAlgorithm(ffi::SSL_SIGN_RSA_PKCS1_SHA256 as _); + + pub const RSA_PKCS1_SHA384: SslSignatureAlgorithm = + SslSignatureAlgorithm(ffi::SSL_SIGN_RSA_PKCS1_SHA384 as _); + + pub const RSA_PKCS1_SHA512: SslSignatureAlgorithm = + SslSignatureAlgorithm(ffi::SSL_SIGN_RSA_PKCS1_SHA512 as _); + + pub const RSA_PKCS1_MD5_SHA1: SslSignatureAlgorithm = + SslSignatureAlgorithm(ffi::SSL_SIGN_RSA_PKCS1_MD5_SHA1 as _); + + pub const ECDSA_SHA1: SslSignatureAlgorithm = + SslSignatureAlgorithm(ffi::SSL_SIGN_ECDSA_SHA1 as _); + + pub const ECDSA_SECP256R1_SHA256: SslSignatureAlgorithm = + SslSignatureAlgorithm(ffi::SSL_SIGN_ECDSA_SECP256R1_SHA256 as _); + + pub const ECDSA_SECP384R1_SHA384: SslSignatureAlgorithm = + SslSignatureAlgorithm(ffi::SSL_SIGN_ECDSA_SECP384R1_SHA384 as _); + + pub const ECDSA_SECP521R1_SHA512: SslSignatureAlgorithm = + SslSignatureAlgorithm(ffi::SSL_SIGN_ECDSA_SECP521R1_SHA512 as _); + + pub const RSA_PSS_RSAE_SHA256: SslSignatureAlgorithm = + SslSignatureAlgorithm(ffi::SSL_SIGN_RSA_PSS_RSAE_SHA256 as _); + + pub const RSA_PSS_RSAE_SHA384: SslSignatureAlgorithm = + SslSignatureAlgorithm(ffi::SSL_SIGN_RSA_PSS_RSAE_SHA384 as _); + + pub const RSA_PSS_RSAE_SHA512: SslSignatureAlgorithm = + SslSignatureAlgorithm(ffi::SSL_SIGN_RSA_PSS_RSAE_SHA512 as _); + + pub const ED25519: SslSignatureAlgorithm = SslSignatureAlgorithm(ffi::SSL_SIGN_ED25519 as _); +} + +/// A TLS Curve. +#[cfg(not(feature = "kx-safe-default"))] +#[repr(transparent)] +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +pub struct SslCurve(c_int); + +#[cfg(not(feature = "kx-safe-default"))] +impl SslCurve { + pub const SECP224R1: SslCurve = SslCurve(ffi::NID_secp224r1); + + pub const SECP256R1: SslCurve = SslCurve(ffi::NID_X9_62_prime256v1); + + pub const SECP384R1: SslCurve = SslCurve(ffi::NID_secp384r1); + + pub const SECP521R1: SslCurve = SslCurve(ffi::NID_secp521r1); + + pub const X25519: SslCurve = SslCurve(ffi::NID_X25519); + + #[cfg(not(any(feature = "fips", feature = "fips-link-precompiled")))] + pub const X25519_KYBER768_DRAFT00: SslCurve = SslCurve(ffi::NID_X25519Kyber768Draft00); + + #[cfg(feature = "pq-experimental")] + pub const X25519_KYBER768_DRAFT00_OLD: SslCurve = SslCurve(ffi::NID_X25519Kyber768Draft00Old); + + #[cfg(feature = "pq-experimental")] + pub const X25519_KYBER512_DRAFT00: SslCurve = SslCurve(ffi::NID_X25519Kyber512Draft00); + + #[cfg(feature = "pq-experimental")] + pub const P256_KYBER768_DRAFT00: SslCurve = SslCurve(ffi::NID_P256Kyber768Draft00); +} + +/// A standard implementation of protocol selection for Application Layer Protocol Negotiation +/// (ALPN). +/// +/// `server` should contain the server's list of supported protocols and `client` the client's. They +/// must both be in the ALPN wire format. See the documentation for +/// [`SslContextBuilder::set_alpn_protos`] for details. +/// +/// It will select the first protocol supported by the server which is also supported by the client. +/// +/// This corresponds to [`SSL_select_next_proto`]. +/// +/// [`SslContextBuilder::set_alpn_protos`]: struct.SslContextBuilder.html#method.set_alpn_protos +/// [`SSL_select_next_proto`]: https://www.openssl.org/docs/man1.1.0/ssl/SSL_CTX_set_alpn_protos.html +pub fn select_next_proto<'a>(server: &[u8], client: &'a [u8]) -> Option<&'a [u8]> { + unsafe { + let mut out = ptr::null_mut(); + let mut outlen = 0; + let r = ffi::SSL_select_next_proto( + &mut out, + &mut outlen, + server.as_ptr(), + server.len() as c_uint, + client.as_ptr(), + client.len() as c_uint, + ); + if r == ffi::OPENSSL_NPN_NEGOTIATED { + Some(slice::from_raw_parts(out as *const u8, outlen as usize)) + } else { + None + } + } +} + +#[cfg(feature = "rpk")] +extern "C" fn rpk_verify_failure_callback( + _ssl: *mut ffi::SSL, + _out_alert: *mut u8, +) -> ffi::ssl_verify_result_t { + // Always verify the peer. + ffi::ssl_verify_result_t::ssl_verify_invalid +} + +/// A builder for `SslContext`s. +pub struct SslContextBuilder { + ctx: SslContext, + #[cfg(feature = "rpk")] + is_rpk: bool, +} + +#[cfg(feature = "rpk")] +impl SslContextBuilder { + /// Creates a new `SslContextBuilder` to be used with Raw Public Key. + /// + /// This corresponds to [`SSL_CTX_new`]. + /// + /// [`SSL_CTX_new`]: https://www.openssl.org/docs/manmaster/man3/SSL_CTX_new.html + pub fn new_rpk() -> Result { + unsafe { + init(); + let ctx = cvt_p(ffi::SSL_CTX_new(SslMethod::tls_with_buffer().as_ptr()))?; + + Ok(SslContextBuilder::from_ptr(ctx, true)) + } + } + + /// Sets raw public key certificate in DER format. + pub fn set_rpk_certificate(&mut self, cert: &[u8]) -> Result<(), ErrorStack> { + unsafe { + cvt(ffi::SSL_CTX_set_server_raw_public_key_certificate( + self.as_ptr(), + cert.as_ptr(), + cert.len() as u32, + )) + .map(|_| ()) + } + } + + /// Sets RPK null chain private key. + pub fn set_null_chain_private_key(&mut self, key: &PKeyRef) -> Result<(), ErrorStack> + where + T: HasPrivate, + { + unsafe { + cvt(ffi::SSL_CTX_set_nullchain_and_key( + self.as_ptr(), + key.as_ptr(), + ptr::null_mut(), + )) + .map(|_| ()) + } + } +} + +impl SslContextBuilder { + /// Creates a new `SslContextBuilder`. + /// + /// This corresponds to [`SSL_CTX_new`]. + /// + /// [`SSL_CTX_new`]: https://www.openssl.org/docs/manmaster/man3/SSL_CTX_new.html + pub fn new(method: SslMethod) -> Result { + unsafe { + init(); + let ctx = cvt_p(ffi::SSL_CTX_new(method.as_ptr()))?; + + #[cfg(feature = "rpk")] + { + Ok(SslContextBuilder::from_ptr(ctx, false)) + } + + #[cfg(not(feature = "rpk"))] + { + Ok(SslContextBuilder::from_ptr(ctx)) + } + } + } + + /// Creates an `SslContextBuilder` from a pointer to a raw OpenSSL value. + /// + /// # Safety + /// + /// The caller must ensure that the pointer is valid and uniquely owned by the builder. + #[cfg(feature = "rpk")] + pub unsafe fn from_ptr(ctx: *mut ffi::SSL_CTX, is_rpk: bool) -> SslContextBuilder { + let ctx = SslContext::from_ptr(ctx); + let mut builder = SslContextBuilder { ctx, is_rpk }; + + builder.set_ex_data(*RPK_FLAG_INDEX, is_rpk); + + builder + } + + /// Creates an `SslContextBuilder` from a pointer to a raw OpenSSL value. + /// + /// # Safety + /// + /// The caller must ensure that the pointer is valid and uniquely owned by the builder. + #[cfg(not(feature = "rpk"))] + pub unsafe fn from_ptr(ctx: *mut ffi::SSL_CTX) -> SslContextBuilder { + SslContextBuilder { + ctx: SslContext::from_ptr(ctx), + } + } + + /// Returns a pointer to the raw OpenSSL value. + pub fn as_ptr(&self) -> *mut ffi::SSL_CTX { + self.ctx.as_ptr() + } + + /// Configures the certificate verification method for new connections. + /// + /// This corresponds to [`SSL_CTX_set_verify`]. + /// + /// [`SSL_CTX_set_verify`]: https://www.openssl.org/docs/man1.1.0/ssl/SSL_CTX_set_verify.html + pub fn set_verify(&mut self, mode: SslVerifyMode) { + #[cfg(feature = "rpk")] + assert!(!self.is_rpk, "This API is not supported for RPK"); + + unsafe { + ffi::SSL_CTX_set_verify(self.as_ptr(), mode.bits() as c_int, None); + } + } + + /// Configures the certificate verification method for new connections and + /// registers a verification callback. + /// + /// The callback is passed a boolean indicating if OpenSSL's internal verification succeeded as + /// well as a reference to the `X509StoreContext` which can be used to examine the certificate + /// chain. It should return a boolean indicating if verification succeeded. + /// + /// This corresponds to [`SSL_CTX_set_verify`]. + /// + /// [`SSL_CTX_set_verify`]: https://www.openssl.org/docs/man1.1.0/ssl/SSL_CTX_set_verify.html + pub fn set_verify_callback(&mut self, mode: SslVerifyMode, verify: F) + where + F: Fn(bool, &mut X509StoreContextRef) -> bool + 'static + Sync + Send, + { + #[cfg(feature = "rpk")] + assert!(!self.is_rpk, "This API is not supported for RPK"); + + unsafe { + self.set_ex_data(SslContext::cached_ex_index::(), verify); + ffi::SSL_CTX_set_verify(self.as_ptr(), mode.bits() as c_int, Some(raw_verify::)); + } + } + + /// Configures the server name indication (SNI) callback for new connections. + /// + /// SNI is used to allow a single server to handle requests for multiple domains, each of which + /// has its own certificate chain and configuration. + /// + /// Obtain the server name with the `servername` method and then set the corresponding context + /// with `set_ssl_context` + /// + /// This corresponds to [`SSL_CTX_set_tlsext_servername_callback`]. + /// + /// [`SSL_CTX_set_tlsext_servername_callback`]: https://www.openssl.org/docs/manmaster/man3/SSL_CTX_set_tlsext_servername_callback.html + // FIXME tlsext prefix? + pub fn set_servername_callback(&mut self, callback: F) + where + F: Fn(&mut SslRef, &mut SslAlert) -> Result<(), SniError> + 'static + Sync + Send, + { + unsafe { + // The SNI callback is somewhat unique in that the callback associated with the original + // context associated with an SSL can be used even if the SSL's context has been swapped + // out. When that happens, we wouldn't be able to look up the callback's state in the + // context's ex data. Instead, pass the pointer directly as the servername arg. It's + // still stored in ex data to manage the lifetime. + let arg = self.set_ex_data_inner(SslContext::cached_ex_index::(), callback); + + ffi::SSL_CTX_set_tlsext_servername_arg(self.as_ptr(), arg); + ffi::SSL_CTX_set_tlsext_servername_callback(self.as_ptr(), Some(raw_sni::)); + } + } + + /// Sets the certificate verification depth. + /// + /// If the peer's certificate chain is longer than this value, verification will fail. + /// + /// This corresponds to [`SSL_CTX_set_verify_depth`]. + /// + /// [`SSL_CTX_set_verify_depth`]: https://www.openssl.org/docs/man1.1.0/ssl/SSL_CTX_set_verify_depth.html + pub fn set_verify_depth(&mut self, depth: u32) { + #[cfg(feature = "rpk")] + assert!(!self.is_rpk, "This API is not supported for RPK"); + + unsafe { + ffi::SSL_CTX_set_verify_depth(self.as_ptr(), depth as c_int); + } + } + + /// Sets a custom certificate store for verifying peer certificates. + /// + /// This corresponds to [`SSL_CTX_set0_verify_cert_store`]. + /// + /// [`SSL_CTX_set0_verify_cert_store`]: https://www.openssl.org/docs/man1.0.2/ssl/SSL_CTX_set0_verify_cert_store.html + pub fn set_verify_cert_store(&mut self, cert_store: X509Store) -> Result<(), ErrorStack> { + #[cfg(feature = "rpk")] + assert!(!self.is_rpk, "This API is not supported for RPK"); + + unsafe { + let ptr = cert_store.as_ptr(); + cvt(ffi::SSL_CTX_set0_verify_cert_store(self.as_ptr(), ptr) as c_int)?; + mem::forget(cert_store); + + Ok(()) + } + } + + /// Replaces the context's certificate store. + /// + /// This corresponds to [`SSL_CTX_set_cert_store`]. + /// + /// [`SSL_CTX_set_cert_store`]: https://www.openssl.org/docs/man1.0.2/man3/SSL_CTX_set_cert_store.html + pub fn set_cert_store(&mut self, cert_store: X509Store) { + #[cfg(feature = "rpk")] + assert!(!self.is_rpk, "This API is not supported for RPK"); + + unsafe { + ffi::SSL_CTX_set_cert_store(self.as_ptr(), cert_store.as_ptr()); + mem::forget(cert_store); + } + } + + /// Controls read ahead behavior. + /// + /// If enabled, OpenSSL will read as much data as is available from the underlying stream, + /// instead of a single record at a time. + /// + /// It has no effect when used with DTLS. + /// + /// This corresponds to [`SSL_CTX_set_read_ahead`]. + /// + /// [`SSL_CTX_set_read_ahead`]: https://www.openssl.org/docs/man1.1.0/ssl/SSL_CTX_set_read_ahead.html + pub fn set_read_ahead(&mut self, read_ahead: bool) { + unsafe { + ffi::SSL_CTX_set_read_ahead(self.as_ptr(), read_ahead as c_int); + } + } + + /// Sets the mode used by the context, returning the previous mode. + /// + /// This corresponds to [`SSL_CTX_set_mode`]. + /// + /// [`SSL_CTX_set_mode`]: https://www.openssl.org/docs/man1.0.2/ssl/SSL_CTX_set_mode.html + pub fn set_mode(&mut self, mode: SslMode) -> SslMode { + unsafe { + let bits = ffi::SSL_CTX_set_mode(self.as_ptr(), mode.bits()); + SslMode::from_bits_retain(bits) + } + } + + /// Sets the parameters to be used during ephemeral Diffie-Hellman key exchange. + /// + /// This corresponds to [`SSL_CTX_set_tmp_dh`]. + /// + /// [`SSL_CTX_set_tmp_dh`]: https://www.openssl.org/docs/man1.0.2/ssl/SSL_CTX_set_tmp_dh.html + pub fn set_tmp_dh(&mut self, dh: &DhRef) -> Result<(), ErrorStack> { + unsafe { cvt(ffi::SSL_CTX_set_tmp_dh(self.as_ptr(), dh.as_ptr()) as c_int).map(|_| ()) } + } + + /// Sets the parameters to be used during ephemeral elliptic curve Diffie-Hellman key exchange. + /// + /// This corresponds to `SSL_CTX_set_tmp_ecdh`. + pub fn set_tmp_ecdh(&mut self, key: &EcKeyRef) -> Result<(), ErrorStack> { + unsafe { cvt(ffi::SSL_CTX_set_tmp_ecdh(self.as_ptr(), key.as_ptr()) as c_int).map(|_| ()) } + } + + /// Use the default locations of trusted certificates for verification. + /// + /// These locations are read from the `SSL_CERT_FILE` and `SSL_CERT_DIR` environment variables + /// if present, or defaults specified at OpenSSL build time otherwise. + /// + /// This corresponds to [`SSL_CTX_set_default_verify_paths`]. + /// + /// [`SSL_CTX_set_default_verify_paths`]: https://www.openssl.org/docs/man1.1.0/ssl/SSL_CTX_set_default_verify_paths.html + pub fn set_default_verify_paths(&mut self) -> Result<(), ErrorStack> { + #[cfg(feature = "rpk")] + assert!(!self.is_rpk, "This API is not supported for RPK"); + + unsafe { cvt(ffi::SSL_CTX_set_default_verify_paths(self.as_ptr())).map(|_| ()) } + } + + /// Loads trusted root certificates from a file. + /// + /// The file should contain a sequence of PEM-formatted CA certificates. + /// + /// This corresponds to [`SSL_CTX_load_verify_locations`]. + /// + /// [`SSL_CTX_load_verify_locations`]: https://www.openssl.org/docs/man1.0.2/ssl/SSL_CTX_load_verify_locations.html + pub fn set_ca_file>(&mut self, file: P) -> Result<(), ErrorStack> { + #[cfg(feature = "rpk")] + assert!(!self.is_rpk, "This API is not supported for RPK"); + + let file = CString::new(file.as_ref().as_os_str().to_str().unwrap()).unwrap(); + unsafe { + cvt(ffi::SSL_CTX_load_verify_locations( + self.as_ptr(), + file.as_ptr() as *const _, + ptr::null(), + )) + .map(|_| ()) + } + } + + /// Sets the list of CA names sent to the client. + /// + /// The CA certificates must still be added to the trust root - they are not automatically set + /// as trusted by this method. + /// + /// This corresponds to [`SSL_CTX_set_client_CA_list`]. + /// + /// [`SSL_CTX_set_client_CA_list`]: https://www.openssl.org/docs/manmaster/man3/SSL_CTX_set_client_CA_list.html + pub fn set_client_ca_list(&mut self, list: Stack) { + #[cfg(feature = "rpk")] + assert!(!self.is_rpk, "This API is not supported for RPK"); + + unsafe { + ffi::SSL_CTX_set_client_CA_list(self.as_ptr(), list.as_ptr()); + mem::forget(list); + } + } + + /// Add the provided CA certificate to the list sent by the server to the client when + /// requesting client-side TLS authentication. + /// + /// This corresponds to [`SSL_CTX_add_client_CA`]. + /// + /// [`SSL_CTX_add_client_CA`]: https://www.openssl.org/docs/man1.0.2/man3/SSL_CTX_set_client_CA_list.html + pub fn add_client_ca(&mut self, cacert: &X509Ref) -> Result<(), ErrorStack> { + #[cfg(feature = "rpk")] + assert!(!self.is_rpk, "This API is not supported for RPK"); + + unsafe { cvt(ffi::SSL_CTX_add_client_CA(self.as_ptr(), cacert.as_ptr())).map(|_| ()) } + } + + /// Set the context identifier for sessions. + /// + /// This value identifies the server's session cache to clients, telling them when they're + /// able to reuse sessions. It should be set to a unique value per server, unless multiple + /// servers share a session cache. + /// + /// This value should be set when using client certificates, or each request will fail its + /// handshake and need to be restarted. + /// + /// This corresponds to [`SSL_CTX_set_session_id_context`]. + /// + /// [`SSL_CTX_set_session_id_context`]: https://www.openssl.org/docs/manmaster/man3/SSL_CTX_set_session_id_context.html + pub fn set_session_id_context(&mut self, sid_ctx: &[u8]) -> Result<(), ErrorStack> { + unsafe { + assert!(sid_ctx.len() <= c_uint::max_value() as usize); + cvt(ffi::SSL_CTX_set_session_id_context( + self.as_ptr(), + sid_ctx.as_ptr(), + sid_ctx.len(), + )) + .map(|_| ()) + } + } + + /// Loads a leaf certificate from a file. + /// + /// Only a single certificate will be loaded - use `add_extra_chain_cert` to add the remainder + /// of the certificate chain, or `set_certificate_chain_file` to load the entire chain from a + /// single file. + /// + /// This corresponds to [`SSL_CTX_use_certificate_file`]. + /// + /// [`SSL_CTX_use_certificate_file`]: https://www.openssl.org/docs/man1.0.2/ssl/SSL_CTX_use_certificate_file.html + pub fn set_certificate_file>( + &mut self, + file: P, + file_type: SslFiletype, + ) -> Result<(), ErrorStack> { + #[cfg(feature = "rpk")] + assert!(!self.is_rpk, "This API is not supported for RPK"); + + let file = CString::new(file.as_ref().as_os_str().to_str().unwrap()).unwrap(); + unsafe { + cvt(ffi::SSL_CTX_use_certificate_file( + self.as_ptr(), + file.as_ptr() as *const _, + file_type.as_raw(), + )) + .map(|_| ()) + } + } + + /// Loads a certificate chain from a file. + /// + /// The file should contain a sequence of PEM-formatted certificates, the first being the leaf + /// certificate, and the remainder forming the chain of certificates up to and including the + /// trusted root certificate. + /// + /// This corresponds to [`SSL_CTX_use_certificate_chain_file`]. + /// + /// [`SSL_CTX_use_certificate_chain_file`]: https://www.openssl.org/docs/man1.0.2/ssl/SSL_CTX_use_certificate_file.html + pub fn set_certificate_chain_file>( + &mut self, + file: P, + ) -> Result<(), ErrorStack> { + let file = CString::new(file.as_ref().as_os_str().to_str().unwrap()).unwrap(); + unsafe { + cvt(ffi::SSL_CTX_use_certificate_chain_file( + self.as_ptr(), + file.as_ptr() as *const _, + )) + .map(|_| ()) + } + } + + /// Sets the leaf certificate. + /// + /// Use `add_extra_chain_cert` to add the remainder of the certificate chain. + /// + /// This corresponds to [`SSL_CTX_use_certificate`]. + /// + /// [`SSL_CTX_use_certificate`]: https://www.openssl.org/docs/man1.0.2/ssl/SSL_CTX_use_certificate_file.html + pub fn set_certificate(&mut self, cert: &X509Ref) -> Result<(), ErrorStack> { + unsafe { cvt(ffi::SSL_CTX_use_certificate(self.as_ptr(), cert.as_ptr())).map(|_| ()) } + } + + /// Appends a certificate to the certificate chain. + /// + /// This chain should contain all certificates necessary to go from the certificate specified by + /// `set_certificate` to a trusted root. + /// + /// This corresponds to [`SSL_CTX_add_extra_chain_cert`]. + /// + /// [`SSL_CTX_add_extra_chain_cert`]: https://www.openssl.org/docs/manmaster/man3/SSL_CTX_add_extra_chain_cert.html + pub fn add_extra_chain_cert(&mut self, cert: X509) -> Result<(), ErrorStack> { + #[cfg(feature = "rpk")] + assert!(!self.is_rpk, "This API is not supported for RPK"); + + unsafe { + cvt(ffi::SSL_CTX_add_extra_chain_cert(self.as_ptr(), cert.as_ptr()) as c_int)?; + mem::forget(cert); + Ok(()) + } + } + + /// Loads the private key from a file. + /// + /// This corresponds to [`SSL_CTX_use_PrivateKey_file`]. + /// + /// [`SSL_CTX_use_PrivateKey_file`]: https://www.openssl.org/docs/man1.0.2/ssl/SSL_CTX_use_PrivateKey_file.html + pub fn set_private_key_file>( + &mut self, + file: P, + file_type: SslFiletype, + ) -> Result<(), ErrorStack> { + let file = CString::new(file.as_ref().as_os_str().to_str().unwrap()).unwrap(); + unsafe { + cvt(ffi::SSL_CTX_use_PrivateKey_file( + self.as_ptr(), + file.as_ptr() as *const _, + file_type.as_raw(), + )) + .map(|_| ()) + } + } + + /// Sets the private key. + /// + /// This corresponds to [`SSL_CTX_use_PrivateKey`]. + /// + /// [`SSL_CTX_use_PrivateKey`]: https://www.openssl.org/docs/man1.0.2/ssl/SSL_CTX_use_PrivateKey_file.html + pub fn set_private_key(&mut self, key: &PKeyRef) -> Result<(), ErrorStack> + where + T: HasPrivate, + { + unsafe { cvt(ffi::SSL_CTX_use_PrivateKey(self.as_ptr(), key.as_ptr())).map(|_| ()) } + } + + /// Sets the list of supported ciphers for protocols before TLSv1.3. + /// + /// The `set_ciphersuites` method controls the cipher suites for TLSv1.3. + /// + /// See [`ciphers`] for details on the format. + /// + /// This corresponds to [`SSL_CTX_set_cipher_list`]. + /// + /// [`ciphers`]: https://www.openssl.org/docs/man1.1.0/apps/ciphers.html + /// [`SSL_CTX_set_cipher_list`]: https://www.openssl.org/docs/manmaster/man3/SSL_CTX_set_cipher_list.html + pub fn set_cipher_list(&mut self, cipher_list: &str) -> Result<(), ErrorStack> { + let cipher_list = CString::new(cipher_list).unwrap(); + unsafe { + cvt(ffi::SSL_CTX_set_cipher_list( + self.as_ptr(), + cipher_list.as_ptr() as *const _, + )) + .map(|_| ()) + } + } + + /// Sets the options used by the context, returning the old set. + /// + /// This corresponds to [`SSL_CTX_set_options`]. + /// + /// # Note + /// + /// This *enables* the specified options, but does not disable unspecified options. Use + /// `clear_options` for that. + /// + /// [`SSL_CTX_set_options`]: https://www.openssl.org/docs/manmaster/man3/SSL_CTX_set_options.html + pub fn set_options(&mut self, option: SslOptions) -> SslOptions { + let bits = unsafe { ffi::SSL_CTX_set_options(self.as_ptr(), option.bits()) }; + SslOptions::from_bits_retain(bits) + } + + /// Returns the options used by the context. + /// + /// This corresponds to [`SSL_CTX_get_options`]. + /// + /// [`SSL_CTX_get_options`]: https://www.openssl.org/docs/manmaster/man3/SSL_CTX_set_options.html + pub fn options(&self) -> SslOptions { + let bits = unsafe { ffi::SSL_CTX_get_options(self.as_ptr()) }; + SslOptions::from_bits_retain(bits) + } + + /// Clears the options used by the context, returning the old set. + /// + /// This corresponds to [`SSL_CTX_clear_options`]. + /// + /// [`SSL_CTX_clear_options`]: https://www.openssl.org/docs/manmaster/man3/SSL_CTX_set_options.html + pub fn clear_options(&mut self, option: SslOptions) -> SslOptions { + let bits = unsafe { ffi::SSL_CTX_clear_options(self.as_ptr(), option.bits()) }; + SslOptions::from_bits_retain(bits) + } + + /// Sets the minimum supported protocol version. + /// + /// A value of `None` will enable protocol versions down the the lowest version supported by + /// OpenSSL. + /// + /// This corresponds to [`SSL_CTX_set_min_proto_version`]. + /// + /// [`SSL_CTX_set_min_proto_version`]: https://www.openssl.org/docs/man1.1.0/ssl/SSL_set_min_proto_version.html + pub fn set_min_proto_version(&mut self, version: Option) -> Result<(), ErrorStack> { + unsafe { + cvt(ffi::SSL_CTX_set_min_proto_version( + self.as_ptr(), + version.map_or(0, |v| v.0 as _), + )) + .map(|_| ()) + } + } + + /// Sets the maximum supported protocol version. + /// + /// A value of `None` will enable protocol versions down the the highest version supported by + /// OpenSSL. + /// + /// This corresponds to [`SSL_CTX_set_max_proto_version`]. + /// + /// [`SSL_CTX_set_max_proto_version`]: https://www.openssl.org/docs/man1.1.0/ssl/SSL_set_min_proto_version.html + pub fn set_max_proto_version(&mut self, version: Option) -> Result<(), ErrorStack> { + unsafe { + cvt(ffi::SSL_CTX_set_max_proto_version( + self.as_ptr(), + version.map_or(0, |v| v.0 as _), + )) + .map(|_| ()) + } + } + + /// Gets the minimum supported protocol version. + /// + /// A value of `None` indicates that all versions down the the lowest version supported by + /// OpenSSL are enabled. + /// + /// This corresponds to [`SSL_CTX_get_min_proto_version`]. + /// + /// [`SSL_CTX_get_min_proto_version`]: https://www.openssl.org/docs/man1.1.0/ssl/SSL_set_min_proto_version.html + pub fn min_proto_version(&mut self) -> Option { + unsafe { + let r = ffi::SSL_CTX_get_min_proto_version(self.as_ptr()); + if r == 0 { + None + } else { + Some(SslVersion(r)) + } + } + } + + /// Gets the maximum supported protocol version. + /// + /// A value of `None` indicates that all versions down the the highest version supported by + /// OpenSSL are enabled. + /// + /// This corresponds to [`SSL_CTX_get_max_proto_version`]. + /// + /// [`SSL_CTX_get_max_proto_version`]: https://www.openssl.org/docs/man1.1.0/ssl/SSL_set_min_proto_version.html + pub fn max_proto_version(&mut self) -> Option { + unsafe { + let r = ffi::SSL_CTX_get_max_proto_version(self.as_ptr()); + if r == 0 { + None + } else { + Some(SslVersion(r)) + } + } + } + + /// Sets the protocols to sent to the server for Application Layer Protocol Negotiation (ALPN). + /// + /// The input must be in ALPN "wire format". It consists of a sequence of supported protocol + /// names prefixed by their byte length. For example, the protocol list consisting of `spdy/1` + /// and `http/1.1` is encoded as `b"\x06spdy/1\x08http/1.1"`. The protocols are ordered by + /// preference. + /// + /// This corresponds to [`SSL_CTX_set_alpn_protos`]. + /// + /// [`SSL_CTX_set_alpn_protos`]: https://www.openssl.org/docs/man1.1.0/ssl/SSL_CTX_set_alpn_protos.html + pub fn set_alpn_protos(&mut self, protocols: &[u8]) -> Result<(), ErrorStack> { + unsafe { + #[cfg_attr( + not(any(feature = "fips", feature = "fips-link-precompiled")), + allow(clippy::unnecessary_cast) + )] + { + assert!(protocols.len() <= ProtosLen::max_value() as usize); + } + let r = ffi::SSL_CTX_set_alpn_protos( + self.as_ptr(), + protocols.as_ptr(), + protocols.len() as ProtosLen, + ); + // fun fact, SSL_CTX_set_alpn_protos has a reversed return code D: + if r == 0 { + Ok(()) + } else { + Err(ErrorStack::get()) + } + } + } + + /// Enables the DTLS extension "use_srtp" as defined in RFC5764. + /// + /// This corresponds to [`SSL_CTX_set_tlsext_use_srtp`]. + /// + /// [`SSL_CTX_set_tlsext_use_srtp`]: https://www.openssl.org/docs/man1.1.1/man3/SSL_CTX_set_tlsext_use_srtp.html + pub fn set_tlsext_use_srtp(&mut self, protocols: &str) -> Result<(), ErrorStack> { + unsafe { + let cstr = CString::new(protocols).unwrap(); + + let r = ffi::SSL_CTX_set_tlsext_use_srtp(self.as_ptr(), cstr.as_ptr()); + // fun fact, set_tlsext_use_srtp has a reversed return code D: + if r == 0 { + Ok(()) + } else { + Err(ErrorStack::get()) + } + } + } + + /// Sets the callback used by a server to select a protocol for Application Layer Protocol + /// Negotiation (ALPN). + /// + /// The callback is provided with the client's protocol list in ALPN wire format. See the + /// documentation for [`SslContextBuilder::set_alpn_protos`] for details. It should return one + /// of those protocols on success. The [`select_next_proto`] function implements the standard + /// protocol selection algorithm. + /// + /// This corresponds to [`SSL_CTX_set_alpn_select_cb`]. + /// + /// [`SslContextBuilder::set_alpn_protos`]: struct.SslContextBuilder.html#method.set_alpn_protos + /// [`select_next_proto`]: fn.select_next_proto.html + /// [`SSL_CTX_set_alpn_select_cb`]: https://www.openssl.org/docs/man1.1.0/ssl/SSL_CTX_set_alpn_protos.html + pub fn set_alpn_select_callback(&mut self, callback: F) + where + F: for<'a> Fn(&mut SslRef, &'a [u8]) -> Result<&'a [u8], AlpnError> + 'static + Sync + Send, + { + unsafe { + self.set_ex_data(SslContext::cached_ex_index::(), callback); + ffi::SSL_CTX_set_alpn_select_cb( + self.as_ptr(), + Some(callbacks::raw_alpn_select::), + ptr::null_mut(), + ); + } + } + + /// Sets a callback that is called before most ClientHello processing and before the decision whether + /// to resume a session is made. The callback may inspect the ClientHello and configure the + /// connection. + /// + /// This corresponds to [`SSL_CTX_set_select_certificate_cb`]. + /// + /// [`SSL_CTX_set_select_certificate_cb`]: https://www.openssl.org/docs/man1.1.0/ssl/SSL_CTX_set_select_certificate_cb.html + pub fn set_select_certificate_callback(&mut self, callback: F) + where + F: Fn(ClientHello<'_>) -> Result<(), SelectCertError> + Sync + Send + 'static, + { + unsafe { + self.set_ex_data(SslContext::cached_ex_index::(), callback); + ffi::SSL_CTX_set_select_certificate_cb( + self.as_ptr(), + Some(callbacks::raw_select_cert::), + ); + } + } + + /// Configures a custom private key method on the context. + /// + /// See [`PrivateKeyMethod`] for more details. + /// + /// This corresponds to [`SSL_CTX_set_private_key_method`] + /// + /// [`SSL_CTX_set_private_key_method`]: https://commondatastorage.googleapis.com/chromium-boringssl-docs/ssl.h.html#SSL_CTX_set_private_key_method + pub fn set_private_key_method(&mut self, method: M) + where + M: PrivateKeyMethod, + { + unsafe { + self.set_ex_data(SslContext::cached_ex_index::(), method); + + ffi::SSL_CTX_set_private_key_method( + self.as_ptr(), + &ffi::SSL_PRIVATE_KEY_METHOD { + sign: Some(callbacks::raw_sign::), + decrypt: Some(callbacks::raw_decrypt::), + complete: Some(callbacks::raw_complete::), + }, + ) + } + } + + /// Checks for consistency between the private key and certificate. + /// + /// This corresponds to [`SSL_CTX_check_private_key`]. + /// + /// [`SSL_CTX_check_private_key`]: https://www.openssl.org/docs/man1.1.0/ssl/SSL_CTX_check_private_key.html + pub fn check_private_key(&self) -> Result<(), ErrorStack> { + unsafe { cvt(ffi::SSL_CTX_check_private_key(self.as_ptr())).map(|_| ()) } + } + + /// Returns a shared reference to the context's certificate store. + /// + /// This corresponds to [`SSL_CTX_get_cert_store`]. + /// + /// [`SSL_CTX_get_cert_store`]: https://www.openssl.org/docs/man1.1.0/ssl/SSL_CTX_get_cert_store.html + pub fn cert_store(&self) -> &X509StoreBuilderRef { + #[cfg(feature = "rpk")] + assert!(!self.is_rpk, "This API is not supported for RPK"); + + unsafe { X509StoreBuilderRef::from_ptr(ffi::SSL_CTX_get_cert_store(self.as_ptr())) } + } + + /// Returns a mutable reference to the context's certificate store. + /// + /// This corresponds to [`SSL_CTX_get_cert_store`]. + /// + /// [`SSL_CTX_get_cert_store`]: https://www.openssl.org/docs/man1.1.0/ssl/SSL_CTX_get_cert_store.html + pub fn cert_store_mut(&mut self) -> &mut X509StoreBuilderRef { + #[cfg(feature = "rpk")] + assert!(!self.is_rpk, "This API is not supported for RPK"); + + unsafe { X509StoreBuilderRef::from_ptr_mut(ffi::SSL_CTX_get_cert_store(self.as_ptr())) } + } + + /// Sets the callback dealing with OCSP stapling. + /// + /// On the client side, this callback is responsible for validating the OCSP status response + /// returned by the server. The status may be retrieved with the `SslRef::ocsp_status` method. + /// A response of `Ok(true)` indicates that the OCSP status is valid, and a response of + /// `Ok(false)` indicates that the OCSP status is invalid and the handshake should be + /// terminated. + /// + /// On the server side, this callback is resopnsible for setting the OCSP status response to be + /// returned to clients. The status may be set with the `SslRef::set_ocsp_status` method. A + /// response of `Ok(true)` indicates that the OCSP status should be returned to the client, and + /// `Ok(false)` indicates that the status should not be returned to the client. + /// + /// This corresponds to [`SSL_CTX_set_tlsext_status_cb`]. + /// + /// [`SSL_CTX_set_tlsext_status_cb`]: https://www.openssl.org/docs/man1.0.2/ssl/SSL_CTX_set_tlsext_status_cb.html + pub fn set_status_callback(&mut self, callback: F) -> Result<(), ErrorStack> + where + F: Fn(&mut SslRef) -> Result + 'static + Sync + Send, + { + unsafe { + self.set_ex_data(SslContext::cached_ex_index::(), callback); + cvt( + ffi::SSL_CTX_set_tlsext_status_cb(self.as_ptr(), Some(raw_tlsext_status::)) + as c_int, + ) + .map(|_| ()) + } + } + + /// Sets the callback for providing an identity and pre-shared key for a TLS-PSK client. + /// + /// The callback will be called with the SSL context, an identity hint if one was provided + /// by the server, a mutable slice for each of the identity and pre-shared key bytes. The + /// identity must be written as a null-terminated C string. + /// + /// This corresponds to [`SSL_CTX_set_psk_client_callback`]. + /// + /// [`SSL_CTX_set_psk_client_callback`]: https://www.openssl.org/docs/man1.0.2/ssl/SSL_CTX_set_psk_client_callback.html + pub fn set_psk_client_callback(&mut self, callback: F) + where + F: Fn(&mut SslRef, Option<&[u8]>, &mut [u8], &mut [u8]) -> Result + + 'static + + Sync + + Send, + { + unsafe { + self.set_ex_data(SslContext::cached_ex_index::(), callback); + ffi::SSL_CTX_set_psk_client_callback(self.as_ptr(), Some(raw_client_psk::)); + } + } + + #[deprecated(since = "0.10.10", note = "renamed to `set_psk_client_callback`")] + pub fn set_psk_callback(&mut self, callback: F) + where + F: Fn(&mut SslRef, Option<&[u8]>, &mut [u8], &mut [u8]) -> Result + + 'static + + Sync + + Send, + { + self.set_psk_client_callback(callback) + } + + /// Sets the callback for providing an identity and pre-shared key for a TLS-PSK server. + /// + /// The callback will be called with the SSL context, an identity provided by the client, + /// and, a mutable slice for the pre-shared key bytes. The callback returns the number of + /// bytes in the pre-shared key. + /// + /// This corresponds to [`SSL_CTX_set_psk_server_callback`]. + /// + /// [`SSL_CTX_set_psk_server_callback`]: https://www.openssl.org/docs/man1.0.2/ssl/SSL_CTX_set_psk_server_callback.html + pub fn set_psk_server_callback(&mut self, callback: F) + where + F: Fn(&mut SslRef, Option<&[u8]>, &mut [u8]) -> Result + + 'static + + Sync + + Send, + { + unsafe { + self.set_ex_data(SslContext::cached_ex_index::(), callback); + ffi::SSL_CTX_set_psk_server_callback(self.as_ptr(), Some(raw_server_psk::)); + } + } + + /// Sets the callback which is called when new sessions are negotiated. + /// + /// This can be used by clients to implement session caching. While in TLSv1.2 the session is + /// available to access via [`SslRef::session`] immediately after the handshake completes, this + /// is not the case for TLSv1.3. There, a session is not generally available immediately, and + /// the server may provide multiple session tokens to the client over a single session. The new + /// session callback is a portable way to deal with both cases. + /// + /// Note that session caching must be enabled for the callback to be invoked, and it defaults + /// off for clients. [`set_session_cache_mode`] controls that behavior. + /// + /// This corresponds to [`SSL_CTX_sess_set_new_cb`]. + /// + /// [`SslRef::session`]: struct.SslRef.html#method.session + /// [`set_session_cache_mode`]: #method.set_session_cache_mode + /// [`SSL_CTX_sess_set_new_cb`]: https://www.openssl.org/docs/manmaster/man3/SSL_CTX_sess_set_new_cb.html + pub fn set_new_session_callback(&mut self, callback: F) + where + F: Fn(&mut SslRef, SslSession) + 'static + Sync + Send, + { + unsafe { + self.set_ex_data(SslContext::cached_ex_index::(), callback); + ffi::SSL_CTX_sess_set_new_cb(self.as_ptr(), Some(callbacks::raw_new_session::)); + } + } + + /// Sets the callback which is called when sessions are removed from the context. + /// + /// Sessions can be removed because they have timed out or because they are considered faulty. + /// + /// This corresponds to [`SSL_CTX_sess_set_remove_cb`]. + /// + /// [`SSL_CTX_sess_set_remove_cb`]: https://www.openssl.org/docs/manmaster/man3/SSL_CTX_sess_set_new_cb.html + pub fn set_remove_session_callback(&mut self, callback: F) + where + F: Fn(&SslContextRef, &SslSessionRef) + 'static + Sync + Send, + { + unsafe { + self.set_ex_data(SslContext::cached_ex_index::(), callback); + ffi::SSL_CTX_sess_set_remove_cb( + self.as_ptr(), + Some(callbacks::raw_remove_session::), + ); + } + } + + /// Sets the callback which is called when a client proposed to resume a session but it was not + /// found in the internal cache. + /// + /// The callback is passed a reference to the session ID provided by the client. It should + /// return the session corresponding to that ID if available. This is only used for servers, not + /// clients. + /// + /// This corresponds to [`SSL_CTX_sess_set_get_cb`]. + /// + /// # Safety + /// + /// The returned [`SslSession`] must not be associated with a different [`SslContext`]. + /// + /// [`SSL_CTX_sess_set_get_cb`]: https://www.openssl.org/docs/manmaster/man3/SSL_CTX_sess_set_new_cb.html + pub unsafe fn set_get_session_callback(&mut self, callback: F) + where + F: Fn(&mut SslRef, &[u8]) -> Result, GetSessionPendingError> + + 'static + + Sync + + Send, + { + self.set_ex_data(SslContext::cached_ex_index::(), callback); + ffi::SSL_CTX_sess_set_get_cb(self.as_ptr(), Some(callbacks::raw_get_session::)); + } + + /// Sets the TLS key logging callback. + /// + /// The callback is invoked whenever TLS key material is generated, and is passed a line of NSS + /// SSLKEYLOGFILE-formatted text. This can be used by tools like Wireshark to decrypt message + /// traffic. The line does not contain a trailing newline. + /// + /// This corresponds to [`SSL_CTX_set_keylog_callback`]. + /// + /// [`SSL_CTX_set_keylog_callback`]: https://www.openssl.org/docs/manmaster/man3/SSL_CTX_set_keylog_callback.html + pub fn set_keylog_callback(&mut self, callback: F) + where + F: Fn(&SslRef, &str) + 'static + Sync + Send, + { + unsafe { + self.set_ex_data(SslContext::cached_ex_index::(), callback); + ffi::SSL_CTX_set_keylog_callback(self.as_ptr(), Some(callbacks::raw_keylog::)); + } + } + + /// Sets the session caching mode use for connections made with the context. + /// + /// Returns the previous session caching mode. + /// + /// This corresponds to [`SSL_CTX_set_session_cache_mode`]. + /// + /// [`SSL_CTX_set_session_cache_mode`]: https://www.openssl.org/docs/man1.1.0/ssl/SSL_CTX_get_session_cache_mode.html + pub fn set_session_cache_mode(&mut self, mode: SslSessionCacheMode) -> SslSessionCacheMode { + unsafe { + let bits = ffi::SSL_CTX_set_session_cache_mode(self.as_ptr(), mode.bits()); + SslSessionCacheMode::from_bits_retain(bits) + } + } + + /// Sets the extra data at the specified index. + /// + /// This can be used to provide data to callbacks registered with the context. Use the + /// `SslContext::new_ex_index` method to create an `Index`. + /// + /// This corresponds to [`SSL_CTX_set_ex_data`]. + /// + /// [`SSL_CTX_set_ex_data`]: https://www.openssl.org/docs/man1.0.2/ssl/SSL_CTX_set_ex_data.html + pub fn set_ex_data(&mut self, index: Index, data: T) { + self.set_ex_data_inner(index, data); + } + + fn set_ex_data_inner(&mut self, index: Index, data: T) -> *mut c_void { + unsafe { + let data = Box::into_raw(Box::new(data)) as *mut c_void; + ffi::SSL_CTX_set_ex_data(self.as_ptr(), index.as_raw(), data); + data + } + } + + /// Sets the context's session cache size limit, returning the previous limit. + /// + /// A value of 0 means that the cache size is unbounded. + /// + /// This corresponds to [`SSL_CTX_sess_get_cache_size`]. + /// + /// [`SSL_CTX_sess_get_cache_size`]: https://www.openssl.org/docs/man1.0.2/man3/SSL_CTX_sess_set_cache_size.html + #[allow(clippy::useless_conversion)] + pub fn set_session_cache_size(&mut self, size: u32) -> u64 { + unsafe { ffi::SSL_CTX_sess_set_cache_size(self.as_ptr(), size.into()).into() } + } + + /// Sets the context's supported signature algorithms. + /// + /// This corresponds to [`SSL_CTX_set1_sigalgs_list`]. + /// + /// [`SSL_CTX_set1_sigalgs_list`]: https://www.openssl.org/docs/man1.1.0/man3/SSL_CTX_set1_sigalgs_list.html + pub fn set_sigalgs_list(&mut self, sigalgs: &str) -> Result<(), ErrorStack> { + let sigalgs = CString::new(sigalgs).unwrap(); + unsafe { + cvt(ffi::SSL_CTX_set1_sigalgs_list(self.as_ptr(), sigalgs.as_ptr()) as c_int) + .map(|_| ()) + } + } + + /// Set's whether the context should enable GREASE. + /// + /// This corresponds to [`SSL_CTX_set_grease_enabled`] + /// + /// [`SSL_CTX_set_grease_enabled`]: https://commondatastorage.googleapis.com/chromium-boringssl-docs/ssl.h.html#SSL_CTX_set_grease_enabled + pub fn set_grease_enabled(&mut self, enabled: bool) { + unsafe { ffi::SSL_CTX_set_grease_enabled(self.as_ptr(), enabled as _) } + } + + /// Sets the context's supported signature verification algorithms. + /// + /// This corresponds to [`SSL_CTX_set_verify_algorithm_prefs`] + /// + /// [`SSL_CTX_set_verify_algorithm_prefs`]: https://commondatastorage.googleapis.com/chromium-boringssl-docs/ssl.h.html#SSL_CTX_set_verify_algorithm_prefs + pub fn set_verify_algorithm_prefs( + &mut self, + prefs: &[SslSignatureAlgorithm], + ) -> Result<(), ErrorStack> { + unsafe { + cvt_0i(ffi::SSL_CTX_set_verify_algorithm_prefs( + self.as_ptr(), + prefs.as_ptr() as *const _, + prefs.len(), + )) + .map(|_| ()) + } + } + + /// Enables SCT requests on all client SSL handshakes. + /// + /// This corresponds to [`SSL_CTX_enable_signed_cert_timestamps`] + /// + /// [`SSL_CTX_enable_signed_cert_timestamps`]: https://commondatastorage.googleapis.com/chromium-boringssl-docs/ssl.h.html#SSL_CTX_enable_signed_cert_timestamps + pub fn enable_signed_cert_timestamps(&mut self) { + unsafe { ffi::SSL_CTX_enable_signed_cert_timestamps(self.as_ptr()) } + } + + /// Enables OCSP stapling on all client SSL handshakes. + /// + /// This corresponds to [`SSL_CTX_enable_ocsp_stapling`] + /// + /// [`SSL_CTX_enable_ocsp_stapling`]: https://commondatastorage.googleapis.com/chromium-boringssl-docs/ssl.h.html#SSL_CTX_enable_ocsp_stapling + pub fn enable_ocsp_stapling(&mut self) { + unsafe { ffi::SSL_CTX_enable_ocsp_stapling(self.as_ptr()) } + } + + /// Sets the context's supported curves. + /// + /// This corresponds to [`SSL_CTX_set1_curves`] + /// + /// [`SSL_CTX_set1_curves`]: https://commondatastorage.googleapis.com/chromium-boringssl-docs/ssl.h.html#SSL_CTX_set1_curves + // + // If the "kx-*" flags are used to set key exchange preference, then don't allow the user to + // set them here. This ensures we don't override the user's preference without telling them: + // when the flags are used, the preferences are set just before connecting or accepting. + #[cfg(not(feature = "kx-safe-default"))] + pub fn set_curves(&mut self, curves: &[SslCurve]) -> Result<(), ErrorStack> { + unsafe { + cvt_0i(ffi::SSL_CTX_set1_curves( + self.as_ptr(), + curves.as_ptr() as *const _, + curves.len(), + )) + .map(|_| ()) + } + } + + /// Consumes the builder, returning a new `SslContext`. + pub fn build(self) -> SslContext { + self.ctx + } +} + +foreign_type_and_impl_send_sync! { + type CType = ffi::SSL_CTX; + fn drop = ffi::SSL_CTX_free; + + /// A context object for TLS streams. + /// + /// Applications commonly configure a single `SslContext` that is shared by all of its + /// `SslStreams`. + pub struct SslContext; +} + +impl Clone for SslContext { + fn clone(&self) -> Self { + (**self).to_owned() + } +} + +impl ToOwned for SslContextRef { + type Owned = SslContext; + + fn to_owned(&self) -> Self::Owned { + unsafe { + SSL_CTX_up_ref(self.as_ptr()); + SslContext::from_ptr(self.as_ptr()) + } + } +} + +// TODO: add useful info here +impl fmt::Debug for SslContext { + fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { + write!(fmt, "SslContext") + } +} + +impl SslContext { + /// Creates a new builder object for an `SslContext`. + pub fn builder(method: SslMethod) -> Result { + SslContextBuilder::new(method) + } + + /// Returns a new extra data index. + /// + /// Each invocation of this function is guaranteed to return a distinct index. These can be used + /// to store data in the context that can be retrieved later by callbacks, for example. + /// + /// This corresponds to [`SSL_CTX_get_ex_new_index`]. + /// + /// [`SSL_CTX_get_ex_new_index`]: https://www.openssl.org/docs/man1.0.2/ssl/SSL_CTX_get_ex_new_index.html + pub fn new_ex_index() -> Result, ErrorStack> + where + T: 'static + Sync + Send, + { + unsafe { + ffi::init(); + let idx = cvt_n(get_new_idx(Some(free_data_box::)))?; + Ok(Index::from_raw(idx)) + } + } + + // FIXME should return a result? + fn cached_ex_index() -> Index + where + T: 'static + Sync + Send, + { + unsafe { + let idx = *INDEXES + .lock() + .unwrap_or_else(|e| e.into_inner()) + .entry(TypeId::of::()) + .or_insert_with(|| SslContext::new_ex_index::().unwrap().as_raw()); + Index::from_raw(idx) + } + } +} + +impl SslContextRef { + /// Returns the certificate associated with this `SslContext`, if present. + /// + /// This corresponds to [`SSL_CTX_get0_certificate`]. + /// + /// [`SSL_CTX_get0_certificate`]: https://www.openssl.org/docs/man1.1.0/ssl/ssl.html + pub fn certificate(&self) -> Option<&X509Ref> { + #[cfg(feature = "rpk")] + assert!(!self.is_rpk(), "This API is not supported for RPK"); + + unsafe { + let ptr = ffi::SSL_CTX_get0_certificate(self.as_ptr()); + if ptr.is_null() { + None + } else { + Some(X509Ref::from_ptr(ptr)) + } + } + } + + /// Returns the private key associated with this `SslContext`, if present. + /// + /// This corresponds to [`SSL_CTX_get0_privatekey`]. + /// + /// [`SSL_CTX_get0_privatekey`]: https://www.openssl.org/docs/man1.1.0/ssl/ssl.html + pub fn private_key(&self) -> Option<&PKeyRef> { + unsafe { + let ptr = ffi::SSL_CTX_get0_privatekey(self.as_ptr()); + if ptr.is_null() { + None + } else { + Some(PKeyRef::from_ptr(ptr)) + } + } + } + + /// Returns a shared reference to the certificate store used for verification. + /// + /// This corresponds to [`SSL_CTX_get_cert_store`]. + /// + /// [`SSL_CTX_get_cert_store`]: https://www.openssl.org/docs/man1.1.0/ssl/SSL_CTX_get_cert_store.html + pub fn cert_store(&self) -> &X509StoreRef { + #[cfg(feature = "rpk")] + assert!(!self.is_rpk(), "This API is not supported for RPK"); + + unsafe { X509StoreRef::from_ptr(ffi::SSL_CTX_get_cert_store(self.as_ptr())) } + } + + /// Returns a shared reference to the stack of certificates making up the chain from the leaf. + /// + /// This corresponds to `SSL_CTX_get_extra_chain_certs`. + pub fn extra_chain_certs(&self) -> &StackRef { + unsafe { + let mut chain = ptr::null_mut(); + ffi::SSL_CTX_get_extra_chain_certs(self.as_ptr(), &mut chain); + assert!(!chain.is_null()); + StackRef::from_ptr(chain) + } + } + + /// Returns a reference to the extra data at the specified index. + /// + /// This corresponds to [`SSL_CTX_get_ex_data`]. + /// + /// [`SSL_CTX_get_ex_data`]: https://www.openssl.org/docs/manmaster/man3/SSL_CTX_get_ex_data.html + pub fn ex_data(&self, index: Index) -> Option<&T> { + unsafe { + let data = ffi::SSL_CTX_get_ex_data(self.as_ptr(), index.as_raw()); + if data.is_null() { + None + } else { + Some(&*(data as *const T)) + } + } + } + + /// Adds a session to the context's cache. + /// + /// Returns `true` if the session was successfully added to the cache, and `false` if it was already present. + /// + /// This corresponds to [`SSL_CTX_add_session`]. + /// + /// # Safety + /// + /// The caller of this method is responsible for ensuring that the session has never been used with another + /// `SslContext` than this one. + /// + /// [`SSL_CTX_add_session`]: https://www.openssl.org/docs/man1.1.1/man3/SSL_CTX_remove_session.html + pub unsafe fn add_session(&self, session: &SslSessionRef) -> bool { + ffi::SSL_CTX_add_session(self.as_ptr(), session.as_ptr()) != 0 + } + + /// Removes a session from the context's cache and marks it as non-resumable. + /// + /// Returns `true` if the session was successfully found and removed, and `false` otherwise. + /// + /// This corresponds to [`SSL_CTX_remove_session`]. + /// + /// # Safety + /// + /// The caller of this method is responsible for ensuring that the session has never been used with another + /// `SslContext` than this one. + /// + /// [`SSL_CTX_remove_session`]: https://www.openssl.org/docs/man1.1.1/man3/SSL_CTX_remove_session.html + pub unsafe fn remove_session(&self, session: &SslSessionRef) -> bool { + ffi::SSL_CTX_remove_session(self.as_ptr(), session.as_ptr()) != 0 + } + + /// Returns the context's session cache size limit. + /// + /// A value of 0 means that the cache size is unbounded. + /// + /// This corresponds to [`SSL_CTX_sess_get_cache_size`]. + /// + /// [`SSL_CTX_sess_get_cache_size`]: https://www.openssl.org/docs/man1.0.2/man3/SSL_CTX_sess_set_cache_size.html + #[allow(clippy::useless_conversion)] + pub fn session_cache_size(&self) -> u64 { + unsafe { ffi::SSL_CTX_sess_get_cache_size(self.as_ptr()).into() } + } + + /// Returns the verify mode that was set on this context from [`SslContextBuilder::set_verify`]. + /// + /// This corresponds to [`SSL_CTX_get_verify_mode`]. + /// + /// [`SslContextBuilder::set_verify`]: struct.SslContextBuilder.html#method.set_verify + /// [`SSL_CTX_get_verify_mode`]: https://www.openssl.org/docs/man1.1.1/man3/SSL_CTX_get_verify_mode.html + pub fn verify_mode(&self) -> SslVerifyMode { + #[cfg(feature = "rpk")] + assert!(!self.is_rpk(), "This API is not supported for RPK"); + + let mode = unsafe { ffi::SSL_CTX_get_verify_mode(self.as_ptr()) }; + SslVerifyMode::from_bits(mode).expect("SSL_CTX_get_verify_mode returned invalid mode") + } + + /// Returns `true` if context was created for Raw Public Key verification + #[cfg(feature = "rpk")] + pub fn is_rpk(&self) -> bool { + self.ex_data(*RPK_FLAG_INDEX).copied().unwrap_or_default() + } +} + +/// Error returned by the callback to get a session when operation +/// could not complete and should be retried later. +/// +/// See [`SslContextBuilder::set_get_session_callback`]. +#[derive(Debug)] +pub struct GetSessionPendingError; + +#[cfg(not(any(feature = "fips", feature = "fips-link-precompiled")))] +type ProtosLen = usize; +#[cfg(any(feature = "fips", feature = "fips-link-precompiled"))] +type ProtosLen = libc::c_uint; + +/// Information about the state of a cipher. +pub struct CipherBits { + /// The number of secret bits used for the cipher. + pub secret: i32, + + /// The number of bits processed by the chosen algorithm. + pub algorithm: i32, +} + +#[repr(transparent)] +pub struct ClientHello<'ssl>(&'ssl ffi::SSL_CLIENT_HELLO); + +impl ClientHello<'_> { + /// Returns the data of a given extension, if present. + /// + /// This corresponds to [`SSL_early_callback_ctx_extension_get`]. + /// + /// [`SSL_early_callback_ctx_extension_get`]: https://commondatastorage.googleapis.com/chromium-boringssl-docs/ssl.h.html#SSL_early_callback_ctx_extension_get + pub fn get_extension(&self, ext_type: ExtensionType) -> Option<&[u8]> { + unsafe { + let mut ptr = ptr::null(); + let mut len = 0; + let result = + ffi::SSL_early_callback_ctx_extension_get(self.0, ext_type.0, &mut ptr, &mut len); + if result == 0 { + return None; + } + Some(slice::from_raw_parts(ptr, len)) + } + } + + pub fn ssl_mut(&mut self) -> &mut SslRef { + unsafe { SslRef::from_ptr_mut(self.0.ssl) } + } + + pub fn ssl(&self) -> &SslRef { + unsafe { SslRef::from_ptr(self.0.ssl) } + } + + /// Returns the servername sent by the client via Server Name Indication (SNI). + pub fn servername(&self, type_: NameType) -> Option<&str> { + self.ssl().servername(type_) + } + + /// Returns the version sent by the client in its Client Hello record. + pub fn client_version(&self) -> SslVersion { + SslVersion(self.0.version) + } + + /// Returns a string describing the protocol version of the connection. + pub fn version_str(&self) -> &'static str { + self.ssl().version_str() + } +} + +/// Information about a cipher. +pub struct SslCipher(*mut ffi::SSL_CIPHER); + +unsafe impl ForeignType for SslCipher { + type CType = ffi::SSL_CIPHER; + type Ref = SslCipherRef; + + #[inline] + unsafe fn from_ptr(ptr: *mut ffi::SSL_CIPHER) -> SslCipher { + SslCipher(ptr) + } + + #[inline] + fn as_ptr(&self) -> *mut ffi::SSL_CIPHER { + self.0 + } +} + +impl Deref for SslCipher { + type Target = SslCipherRef; + + fn deref(&self) -> &SslCipherRef { + unsafe { SslCipherRef::from_ptr(self.0) } + } +} + +impl DerefMut for SslCipher { + fn deref_mut(&mut self) -> &mut SslCipherRef { + unsafe { SslCipherRef::from_ptr_mut(self.0) } + } +} + +/// Reference to an [`SslCipher`]. +/// +/// [`SslCipher`]: struct.SslCipher.html +pub struct SslCipherRef(Opaque); + +unsafe impl ForeignTypeRef for SslCipherRef { + type CType = ffi::SSL_CIPHER; +} + +impl SslCipherRef { + /// Returns the name of the cipher. + /// + /// This corresponds to [`SSL_CIPHER_get_name`]. + /// + /// [`SSL_CIPHER_get_name`]: https://www.openssl.org/docs/manmaster/man3/SSL_CIPHER_get_name.html + pub fn name(&self) -> &'static str { + unsafe { + let ptr = ffi::SSL_CIPHER_get_name(self.as_ptr()); + CStr::from_ptr(ptr).to_str().unwrap() + } + } + + /// Returns the RFC-standard name of the cipher, if one exists. + /// + /// This corresponds to [`SSL_CIPHER_standard_name`]. + /// + /// [`SSL_CIPHER_standard_name`]: https://www.openssl.org/docs/manmaster/man3/SSL_CIPHER_get_name.html + pub fn standard_name(&self) -> Option<&'static str> { + unsafe { + let ptr = ffi::SSL_CIPHER_standard_name(self.as_ptr()); + if ptr.is_null() { + None + } else { + Some(CStr::from_ptr(ptr).to_str().unwrap()) + } + } + } + + /// Returns the SSL/TLS protocol version that first defined the cipher. + /// + /// This corresponds to [`SSL_CIPHER_get_version`]. + /// + /// [`SSL_CIPHER_get_version`]: https://www.openssl.org/docs/manmaster/man3/SSL_CIPHER_get_name.html + pub fn version(&self) -> &'static str { + let version = unsafe { + let ptr = ffi::SSL_CIPHER_get_version(self.as_ptr()); + CStr::from_ptr(ptr as *const _) + }; + + str::from_utf8(version.to_bytes()).unwrap() + } + + /// Returns the number of bits used for the cipher. + /// + /// This corresponds to [`SSL_CIPHER_get_bits`]. + /// + /// [`SSL_CIPHER_get_bits`]: https://www.openssl.org/docs/manmaster/man3/SSL_CIPHER_get_name.html + #[allow(clippy::useless_conversion)] + pub fn bits(&self) -> CipherBits { + unsafe { + let mut algo_bits = 0; + let secret_bits = ffi::SSL_CIPHER_get_bits(self.as_ptr(), &mut algo_bits); + CipherBits { + secret: secret_bits.into(), + algorithm: algo_bits.into(), + } + } + } + + /// Returns a textual description of the cipher. + /// + /// This corresponds to [`SSL_CIPHER_description`]. + /// + /// [`SSL_CIPHER_description`]: https://www.openssl.org/docs/manmaster/man3/SSL_CIPHER_get_name.html + pub fn description(&self) -> String { + unsafe { + // SSL_CIPHER_description requires a buffer of at least 128 bytes. + let mut buf = [0; 128]; + let ptr = ffi::SSL_CIPHER_description(self.as_ptr(), buf.as_mut_ptr(), 128); + String::from_utf8(CStr::from_ptr(ptr as *const _).to_bytes().to_vec()).unwrap() + } + } + + /// Returns the NID corresponding to the cipher. + /// + /// This corresponds to [`SSL_CIPHER_get_cipher_nid`]. + /// + /// [`SSL_CIPHER_get_cipher_nid`]: https://www.openssl.org/docs/man1.1.0/ssl/SSL_CIPHER_get_cipher_nid.html + pub fn cipher_nid(&self) -> Option { + let n = unsafe { ffi::SSL_CIPHER_get_cipher_nid(self.as_ptr()) }; + if n == 0 { + None + } else { + Some(Nid::from_raw(n)) + } + } +} + +foreign_type_and_impl_send_sync! { + type CType = ffi::SSL_SESSION; + fn drop = ffi::SSL_SESSION_free; + + /// An encoded SSL session. + /// + /// These can be cached to share sessions across connections. + pub struct SslSession; +} + +impl Clone for SslSession { + fn clone(&self) -> SslSession { + SslSessionRef::to_owned(self) + } +} + +impl SslSession { + from_der! { + /// Deserializes a DER-encoded session structure. + /// + /// This corresponds to [`d2i_SSL_SESSION`]. + /// + /// [`d2i_SSL_SESSION`]: https://www.openssl.org/docs/man1.0.2/ssl/d2i_SSL_SESSION.html + from_der, + SslSession, + ffi::d2i_SSL_SESSION, + ::libc::c_long + } +} + +impl ToOwned for SslSessionRef { + type Owned = SslSession; + + fn to_owned(&self) -> SslSession { + unsafe { + SSL_SESSION_up_ref(self.as_ptr()); + SslSession(NonNull::new_unchecked(self.as_ptr())) + } + } +} + +impl SslSessionRef { + /// Returns the SSL session ID. + /// + /// This corresponds to [`SSL_SESSION_get_id`]. + /// + /// [`SSL_SESSION_get_id`]: https://www.openssl.org/docs/manmaster/man3/SSL_SESSION_get_id.html + pub fn id(&self) -> &[u8] { + unsafe { + let mut len = 0; + let p = ffi::SSL_SESSION_get_id(self.as_ptr(), &mut len); + slice::from_raw_parts(p, len as usize) + } + } + + /// Returns the length of the master key. + /// + /// This corresponds to [`SSL_SESSION_get_master_key`]. + /// + /// [`SSL_SESSION_get_master_key`]: https://www.openssl.org/docs/man1.1.0/ssl/SSL_SESSION_get_master_key.html + pub fn master_key_len(&self) -> usize { + unsafe { SSL_SESSION_get_master_key(self.as_ptr(), ptr::null_mut(), 0) } + } + + /// Copies the master key into the provided buffer. + /// + /// Returns the number of bytes written, or the size of the master key if the buffer is empty. + /// + /// This corresponds to [`SSL_SESSION_get_master_key`]. + /// + /// [`SSL_SESSION_get_master_key`]: https://www.openssl.org/docs/man1.1.0/ssl/SSL_SESSION_get_master_key.html + pub fn master_key(&self, buf: &mut [u8]) -> usize { + unsafe { SSL_SESSION_get_master_key(self.as_ptr(), buf.as_mut_ptr(), buf.len()) } + } + + /// Returns the time at which the session was established, in seconds since the Unix epoch. + /// + /// This corresponds to [`SSL_SESSION_get_time`]. + /// + /// [`SSL_SESSION_get_time`]: https://www.openssl.org/docs/man1.1.1/man3/SSL_SESSION_get_time.html + #[allow(clippy::useless_conversion)] + pub fn time(&self) -> u64 { + unsafe { ffi::SSL_SESSION_get_time(self.as_ptr()) } + } + + /// Returns the sessions timeout, in seconds. + /// + /// A session older than this time should not be used for session resumption. + /// + /// This corresponds to [`SSL_SESSION_get_timeout`]. + /// + /// [`SSL_SESSION_get_timeout`]: https://www.openssl.org/docs/man1.1.1/man3/SSL_SESSION_get_time.html + #[allow(clippy::useless_conversion)] + pub fn timeout(&self) -> u32 { + unsafe { ffi::SSL_SESSION_get_timeout(self.as_ptr()) } + } + + /// Returns the session's TLS protocol version. + /// + /// This corresponds to [`SSL_SESSION_get_protocol_version`]. + /// + /// [`SSL_SESSION_get_protocol_version`]: https://www.openssl.org/docs/man1.1.1/man3/SSL_SESSION_get_protocol_version.html + pub fn protocol_version(&self) -> SslVersion { + unsafe { + let version = ffi::SSL_SESSION_get_protocol_version(self.as_ptr()); + SslVersion(version) + } + } + + to_der! { + /// Serializes the session into a DER-encoded structure. + /// + /// This corresponds to [`i2d_SSL_SESSION`]. + /// + /// [`i2d_SSL_SESSION`]: https://www.openssl.org/docs/man1.0.2/ssl/i2d_SSL_SESSION.html + to_der, + ffi::i2d_SSL_SESSION + } +} + +foreign_type_and_impl_send_sync! { + type CType = ffi::SSL; + fn drop = ffi::SSL_free; + + /// The state of an SSL/TLS session. + /// + /// `Ssl` objects are created from an [`SslContext`], which provides configuration defaults. + /// These defaults can be overridden on a per-`Ssl` basis, however. + /// + /// [`SslContext`]: struct.SslContext.html + pub struct Ssl; +} + +impl fmt::Debug for Ssl { + fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { + fmt::Debug::fmt(&**self, fmt) + } +} + +impl Ssl { + /// Returns a new extra data index. + /// + /// Each invocation of this function is guaranteed to return a distinct index. These can be used + /// to store data in the context that can be retrieved later by callbacks, for example. + /// + /// This corresponds to [`SSL_get_ex_new_index`]. + /// + /// [`SSL_get_ex_new_index`]: https://www.openssl.org/docs/man1.0.2/ssl/SSL_get_ex_new_index.html + pub fn new_ex_index() -> Result, ErrorStack> + where + T: 'static + Sync + Send, + { + unsafe { + ffi::init(); + let idx = cvt_n(get_new_ssl_idx(Some(free_data_box::)))?; + Ok(Index::from_raw(idx)) + } + } + + // FIXME should return a result? + fn cached_ex_index() -> Index + where + T: 'static + Sync + Send, + { + unsafe { + let idx = *SSL_INDEXES + .lock() + .unwrap_or_else(|e| e.into_inner()) + .entry(TypeId::of::()) + .or_insert_with(|| Ssl::new_ex_index::().unwrap().as_raw()); + Index::from_raw(idx) + } + } + + /// Creates a new `Ssl`. + /// + /// This corresponds to [`SSL_new`]. + /// + /// [`SSL_new`]: https://www.openssl.org/docs/man1.0.2/ssl/SSL_new.html + // FIXME should take &SslContextRef + pub fn new(ctx: &SslContext) -> Result { + unsafe { + let ptr = cvt_p(ffi::SSL_new(ctx.as_ptr()))?; + let mut ssl = Ssl::from_ptr(ptr); + ssl.set_ex_data(*SESSION_CTX_INDEX, ctx.clone()); + + Ok(ssl) + } + } + + /// Creates a new [`Ssl`]. + /// + /// This corresponds to [`SSL_new`](`ffi::SSL_new`). + /// + /// This function does the same as [`Self:new`] except that it takes &[SslContextRef]. + // Both functions exist for backward compatibility (no breaking API). + pub fn new_from_ref(ctx: &SslContextRef) -> Result { + unsafe { + let ptr = cvt_p(ffi::SSL_new(ctx.as_ptr()))?; + let mut ssl = Ssl::from_ptr(ptr); + SSL_CTX_up_ref(ctx.as_ptr()); + let ctx_owned = SslContext::from_ptr(ctx.as_ptr()); + ssl.set_ex_data(*SESSION_CTX_INDEX, ctx_owned); + + Ok(ssl) + } + } + + /// Initiates a client-side TLS handshake, returning a [`MidHandshakeSslStream`]. + /// + /// This method is guaranteed to return without calling any callback defined + /// in the internal [`Ssl`] or [`SslContext`]. + /// + /// See [`SslStreamBuilder::setup_connect`] for more details. + /// + /// # Warning + /// + /// BoringSSL's default configuration is insecure. It is highly recommended to use + /// [`SslConnector`] rather than [`Ssl`] directly, as it manages that configuration. + pub fn setup_connect(self, stream: S) -> MidHandshakeSslStream + where + S: Read + Write, + { + SslStreamBuilder::new(self, stream).setup_connect() + } + + /// Attempts a client-side TLS handshake. + /// + /// This is a convenience method which combines [`Self::setup_connect`] and + /// [`MidHandshakeSslStream::handshake`]. + /// + /// # Warning + /// + /// OpenSSL's default configuration is insecure. It is highly recommended to use + /// [`SslConnector`] rather than `Ssl` directly, as it manages that configuration. + pub fn connect(self, stream: S) -> Result, HandshakeError> + where + S: Read + Write, + { + self.setup_connect(stream).handshake() + } + + /// Initiates a server-side TLS handshake. + /// + /// This method is guaranteed to return without calling any callback defined + /// in the internal [`Ssl`] or [`SslContext`]. + /// + /// See [`SslStreamBuilder::setup_accept`] for more details. + /// + /// # Warning + /// + /// BoringSSL's default configuration is insecure. It is highly recommended to use + /// [`SslAcceptor`] rather than [`Ssl`] directly, as it manages that configuration. + pub fn setup_accept(self, stream: S) -> MidHandshakeSslStream + where + S: Read + Write, + { + #[cfg(feature = "rpk")] + { + let ctx = self.ssl_context(); + + if ctx.is_rpk() { + unsafe { + ffi::SSL_CTX_set_custom_verify( + ctx.as_ptr(), + SslVerifyMode::PEER.bits(), + Some(rpk_verify_failure_callback), + ); + } + } + } + + SslStreamBuilder::new(self, stream).setup_accept() + } + + /// Attempts a server-side TLS handshake. + /// + /// This is a convenience method which combines [`Self::setup_accept`] and + /// [`MidHandshakeSslStream::handshake`]. + /// + /// # Warning + /// + /// OpenSSL's default configuration is insecure. It is highly recommended to use + /// `SslAcceptor` rather than `Ssl` directly, as it manages that configuration. + /// + /// [`SSL_accept`]: https://www.openssl.org/docs/manmaster/man3/SSL_accept.html + pub fn accept(self, stream: S) -> Result, HandshakeError> + where + S: Read + Write, + { + self.setup_accept(stream).handshake() + } +} + +impl fmt::Debug for SslRef { + fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { + let mut builder = fmt.debug_struct("Ssl"); + + builder.field("state", &self.state_string_long()); + + #[cfg(feature = "rpk")] + if !self.ssl_context().is_rpk() { + builder.field("verify_result", &self.verify_result()); + } + + #[cfg(not(feature = "rpk"))] + builder.field("verify_result", &self.verify_result()); + + builder.finish() + } +} + +impl SslRef { + fn get_raw_rbio(&self) -> *mut ffi::BIO { + unsafe { ffi::SSL_get_rbio(self.as_ptr()) } + } + + fn read(&mut self, buf: &mut [u8]) -> c_int { + let len = cmp::min(c_int::max_value() as usize, buf.len()) as c_int; + unsafe { ffi::SSL_read(self.as_ptr(), buf.as_ptr() as *mut c_void, len) } + } + + fn write(&mut self, buf: &[u8]) -> c_int { + let len = cmp::min(c_int::max_value() as usize, buf.len()) as c_int; + unsafe { ffi::SSL_write(self.as_ptr(), buf.as_ptr() as *const c_void, len) } + } + + fn get_error(&self, ret: c_int) -> ErrorCode { + unsafe { ErrorCode::from_raw(ffi::SSL_get_error(self.as_ptr(), ret)) } + } + + #[cfg(feature = "kx-safe-default")] + fn set_curves_list(&mut self, curves: &str) -> Result<(), ErrorStack> { + let curves = CString::new(curves).unwrap(); + unsafe { + cvt(ffi::SSL_set1_curves_list( + self.as_ptr(), + curves.as_ptr() as *const _, + )) + .map(|_| ()) + } + } + + #[cfg(feature = "kx-safe-default")] + fn client_set_default_curves_list(&mut self) { + let curves = if cfg!(feature = "kx-client-pq-preferred") { + if cfg!(feature = "kx-client-nist-required") { + "P256Kyber768Draft00:P-256:P-384:P-521" + } else { + "X25519Kyber768Draft00:X25519:P256Kyber768Draft00:P-256:P-384:P-521" + } + } else if cfg!(feature = "kx-client-pq-supported") { + if cfg!(feature = "kx-client-nist-required") { + "P-256:P-384:P-521:P256Kyber768Draft00" + } else { + "X25519:P-256:P-384:P-521:X25519Kyber768Draft00:P256Kyber768Draft00" + } + } else { + if cfg!(feature = "kx-client-nist-required") { + "P-256:P-384:P-521" + } else { + "X25519:P-256:P-384:P-521" + } + }; + + self.set_curves_list(curves) + .expect("invalid default client curves list"); + } + + #[cfg(feature = "kx-safe-default")] + fn server_set_default_curves_list(&mut self) { + self.set_curves_list("X25519Kyber768Draft00:P256Kyber768Draft00:X25519:P-256:P-384") + .expect("invalid default server curves list"); + } + + /// Like [`SslContextBuilder::set_verify`]. + /// + /// This corresponds to [`SSL_set_verify`]. + /// + /// [`SslContextBuilder::set_verify`]: struct.SslContextBuilder.html#method.set_verify + /// [`SSL_set_verify`]: https://www.openssl.org/docs/man1.0.2/ssl/SSL_set_verify.html + pub fn set_verify(&mut self, mode: SslVerifyMode) { + #[cfg(feature = "rpk")] + assert!( + !self.ssl_context().is_rpk(), + "This API is not supported for RPK" + ); + + unsafe { ffi::SSL_set_verify(self.as_ptr(), mode.bits() as c_int, None) } + } + + /// Returns the verify mode that was set using `set_verify`. + /// + /// This corresponds to [`SSL_get_verify_mode`]. + /// + /// [`SSL_get_verify_mode`]: https://www.openssl.org/docs/man1.1.1/man3/SSL_get_verify_mode.html + pub fn verify_mode(&self) -> SslVerifyMode { + #[cfg(feature = "rpk")] + assert!( + !self.ssl_context().is_rpk(), + "This API is not supported for RPK" + ); + + let mode = unsafe { ffi::SSL_get_verify_mode(self.as_ptr()) }; + SslVerifyMode::from_bits(mode).expect("SSL_get_verify_mode returned invalid mode") + } + + /// Like [`SslContextBuilder::set_verify_callback`]. + /// + /// This corresponds to [`SSL_set_verify`]. + /// + /// [`SslContextBuilder::set_verify_callback`]: struct.SslContextBuilder.html#method.set_verify_callback + /// [`SSL_set_verify`]: https://www.openssl.org/docs/man1.0.2/ssl/SSL_set_verify.html + pub fn set_verify_callback(&mut self, mode: SslVerifyMode, verify: F) + where + F: Fn(bool, &mut X509StoreContextRef) -> bool + 'static + Sync + Send, + { + #[cfg(feature = "rpk")] + assert!( + !self.ssl_context().is_rpk(), + "This API is not supported for RPK" + ); + + unsafe { + // this needs to be in an Arc since the callback can register a new callback! + self.set_ex_data(Ssl::cached_ex_index(), Arc::new(verify)); + ffi::SSL_set_verify( + self.as_ptr(), + mode.bits() as c_int, + Some(ssl_raw_verify::), + ); + } + } + + /// Like [`SslContextBuilder::set_tmp_dh`]. + /// + /// This corresponds to [`SSL_set_tmp_dh`]. + /// + /// [`SslContextBuilder::set_tmp_dh`]: struct.SslContextBuilder.html#method.set_tmp_dh + /// [`SSL_set_tmp_dh`]: https://www.openssl.org/docs/man1.0.2/ssl/SSL_set_tmp_dh.html + pub fn set_tmp_dh(&mut self, dh: &DhRef) -> Result<(), ErrorStack> { + unsafe { cvt(ffi::SSL_set_tmp_dh(self.as_ptr(), dh.as_ptr()) as c_int).map(|_| ()) } + } + + /// Like [`SslContextBuilder::set_tmp_ecdh`]. + /// + /// This corresponds to `SSL_set_tmp_ecdh`. + /// + /// [`SslContextBuilder::set_tmp_ecdh`]: struct.SslContextBuilder.html#method.set_tmp_ecdh + pub fn set_tmp_ecdh(&mut self, key: &EcKeyRef) -> Result<(), ErrorStack> { + unsafe { cvt(ffi::SSL_set_tmp_ecdh(self.as_ptr(), key.as_ptr()) as c_int).map(|_| ()) } + } + + /// Like [`SslContextBuilder::set_alpn_protos`]. + /// + /// This corresponds to [`SSL_set_alpn_protos`]. + /// + /// [`SslContextBuilder::set_alpn_protos`]: struct.SslContextBuilder.html#method.set_alpn_protos + /// [`SSL_set_alpn_protos`]: https://www.openssl.org/docs/man1.1.0/ssl/SSL_set_alpn_protos.html + pub fn set_alpn_protos(&mut self, protocols: &[u8]) -> Result<(), ErrorStack> { + unsafe { + #[cfg_attr( + not(any(feature = "fips", feature = "fips-link-precompiled")), + allow(clippy::unnecessary_cast) + )] + { + assert!(protocols.len() <= ProtosLen::max_value() as usize); + } + let r = ffi::SSL_set_alpn_protos( + self.as_ptr(), + protocols.as_ptr(), + protocols.len() as ProtosLen, + ); + // fun fact, SSL_set_alpn_protos has a reversed return code D: + if r == 0 { + Ok(()) + } else { + Err(ErrorStack::get()) + } + } + } + + /// Returns the current cipher if the session is active. + /// + /// This corresponds to [`SSL_get_current_cipher`]. + /// + /// [`SSL_get_current_cipher`]: https://www.openssl.org/docs/man1.1.0/ssl/SSL_get_current_cipher.html + pub fn current_cipher(&self) -> Option<&SslCipherRef> { + unsafe { + let ptr = ffi::SSL_get_current_cipher(self.as_ptr()); + + if ptr.is_null() { + None + } else { + Some(SslCipherRef::from_ptr(ptr as *mut _)) + } + } + } + + /// Returns a short string describing the state of the session. + /// + /// This corresponds to [`SSL_state_string`]. + /// + /// [`SSL_state_string`]: https://www.openssl.org/docs/man1.0.2/ssl/SSL_state_string.html + pub fn state_string(&self) -> &'static str { + let state = unsafe { + let ptr = ffi::SSL_state_string(self.as_ptr()); + CStr::from_ptr(ptr as *const _) + }; + + str::from_utf8(state.to_bytes()).unwrap() + } + + /// Returns a longer string describing the state of the session. + /// + /// This corresponds to [`SSL_state_string_long`]. + /// + /// [`SSL_state_string_long`]: https://www.openssl.org/docs/man1.1.0/ssl/SSL_state_string_long.html + pub fn state_string_long(&self) -> &'static str { + let state = unsafe { + let ptr = ffi::SSL_state_string_long(self.as_ptr()); + CStr::from_ptr(ptr as *const _) + }; + + str::from_utf8(state.to_bytes()).unwrap() + } + + /// Sets the host name to be sent to the server for Server Name Indication (SNI). + /// + /// It has no effect for a server-side connection. + /// + /// This corresponds to [`SSL_set_tlsext_host_name`]. + /// + /// [`SSL_set_tlsext_host_name`]: https://www.openssl.org/docs/manmaster/man3/SSL_get_servername_type.html + pub fn set_hostname(&mut self, hostname: &str) -> Result<(), ErrorStack> { + let cstr = CString::new(hostname).unwrap(); + unsafe { + cvt(ffi::SSL_set_tlsext_host_name(self.as_ptr(), cstr.as_ptr() as *mut _) as c_int) + .map(|_| ()) + } + } + + /// Returns the peer's certificate, if present. + /// + /// This corresponds to [`SSL_get_peer_certificate`]. + /// + /// [`SSL_get_peer_certificate`]: https://www.openssl.org/docs/man1.1.0/ssl/SSL_get_peer_certificate.html + pub fn peer_certificate(&self) -> Option { + #[cfg(feature = "rpk")] + assert!( + !self.ssl_context().is_rpk(), + "This API is not supported for RPK" + ); + + unsafe { + let ptr = ffi::SSL_get_peer_certificate(self.as_ptr()); + if ptr.is_null() { + None + } else { + Some(X509::from_ptr(ptr)) + } + } + } + + /// Returns the certificate chain of the peer, if present. + /// + /// On the client side, the chain includes the leaf certificate, but on the server side it does + /// not. Fun! + /// + /// This corresponds to [`SSL_get_peer_cert_chain`]. + /// + /// [`SSL_get_peer_cert_chain`]: https://www.openssl.org/docs/man1.1.0/ssl/SSL_get_peer_cert_chain.html + pub fn peer_cert_chain(&self) -> Option<&StackRef> { + #[cfg(feature = "rpk")] + assert!( + !self.ssl_context().is_rpk(), + "This API is not supported for RPK" + ); + + unsafe { + let ptr = ffi::SSL_get_peer_cert_chain(self.as_ptr()); + if ptr.is_null() { + None + } else { + Some(StackRef::from_ptr(ptr)) + } + } + } + + /// Like [`SslContext::certificate`]. + /// + /// This corresponds to `SSL_get_certificate`. + /// + /// [`SslContext::certificate`]: struct.SslContext.html#method.certificate + pub fn certificate(&self) -> Option<&X509Ref> { + #[cfg(feature = "rpk")] + assert!( + !self.ssl_context().is_rpk(), + "This API is not supported for RPK" + ); + + unsafe { + let ptr = ffi::SSL_get_certificate(self.as_ptr()); + if ptr.is_null() { + None + } else { + Some(X509Ref::from_ptr(ptr)) + } + } + } + + /// Like [`SslContext::private_key`]. + /// + /// This corresponds to `SSL_get_privatekey`. + /// + /// [`SslContext::private_key`]: struct.SslContext.html#method.private_key + pub fn private_key(&self) -> Option<&PKeyRef> { + unsafe { + let ptr = ffi::SSL_get_privatekey(self.as_ptr()); + if ptr.is_null() { + None + } else { + Some(PKeyRef::from_ptr(ptr)) + } + } + } + + #[deprecated(since = "0.10.5", note = "renamed to `version_str`")] + pub fn version(&self) -> &str { + self.version_str() + } + + /// Returns the protocol version of the session. + /// + /// This corresponds to [`SSL_version`]. + /// + /// [`SSL_version`]: https://www.openssl.org/docs/manmaster/man3/SSL_version.html + pub fn version2(&self) -> Option { + unsafe { + let r = ffi::SSL_version(self.as_ptr()); + if r == 0 { + None + } else { + r.try_into().ok().map(SslVersion) + } + } + } + + /// Returns a string describing the protocol version of the session. + /// + /// This corresponds to [`SSL_get_version`]. + /// + /// [`SSL_get_version`]: https://www.openssl.org/docs/man1.1.0/ssl/SSL_get_version.html + pub fn version_str(&self) -> &'static str { + let version = unsafe { + let ptr = ffi::SSL_get_version(self.as_ptr()); + CStr::from_ptr(ptr as *const _) + }; + + str::from_utf8(version.to_bytes()).unwrap() + } + + /// Returns the protocol selected via Application Layer Protocol Negotiation (ALPN). + /// + /// The protocol's name is returned is an opaque sequence of bytes. It is up to the client + /// to interpret it. + /// + /// This corresponds to [`SSL_get0_alpn_selected`]. + /// + /// [`SSL_get0_alpn_selected`]: https://www.openssl.org/docs/manmaster/man3/SSL_get0_next_proto_negotiated.html + pub fn selected_alpn_protocol(&self) -> Option<&[u8]> { + unsafe { + let mut data: *const c_uchar = ptr::null(); + let mut len: c_uint = 0; + // Get the negotiated protocol from the SSL instance. + // `data` will point at a `c_uchar` array; `len` will contain the length of this array. + ffi::SSL_get0_alpn_selected(self.as_ptr(), &mut data, &mut len); + + if data.is_null() { + None + } else { + Some(slice::from_raw_parts(data, len as usize)) + } + } + } + + /// Enables the DTLS extension "use_srtp" as defined in RFC5764. + /// + /// This corresponds to [`SSL_set_tlsext_use_srtp`]. + /// + /// [`SSL_set_tlsext_use_srtp`]: https://www.openssl.org/docs/man1.1.1/man3/SSL_CTX_set_tlsext_use_srtp.html + pub fn set_tlsext_use_srtp(&mut self, protocols: &str) -> Result<(), ErrorStack> { + unsafe { + let cstr = CString::new(protocols).unwrap(); + + let r = ffi::SSL_set_tlsext_use_srtp(self.as_ptr(), cstr.as_ptr()); + // fun fact, set_tlsext_use_srtp has a reversed return code D: + if r == 0 { + Ok(()) + } else { + Err(ErrorStack::get()) + } + } + } + + /// Gets all SRTP profiles that are enabled for handshake via set_tlsext_use_srtp + /// + /// DTLS extension "use_srtp" as defined in RFC5764 has to be enabled. + /// + /// This corresponds to [`SSL_get_srtp_profiles`]. + /// + /// [`SSL_get_srtp_profiles`]: https://www.openssl.org/docs/man1.1.1/man3/SSL_CTX_set_tlsext_use_srtp.html + pub fn srtp_profiles(&self) -> Option<&StackRef> { + unsafe { + let chain = ffi::SSL_get_srtp_profiles(self.as_ptr()); + + if chain.is_null() { + None + } else { + Some(StackRef::from_ptr(chain as *mut _)) + } + } + } + + /// Gets the SRTP profile selected by handshake. + /// + /// DTLS extension "use_srtp" as defined in RFC5764 has to be enabled. + /// + /// This corresponds to [`SSL_get_selected_srtp_profile`]. + /// + /// [`SSL_get_selected_srtp_profile`]: https://www.openssl.org/docs/man1.1.1/man3/SSL_CTX_set_tlsext_use_srtp.html + pub fn selected_srtp_profile(&self) -> Option<&SrtpProtectionProfileRef> { + unsafe { + let profile = ffi::SSL_get_selected_srtp_profile(self.as_ptr()); + + if profile.is_null() { + None + } else { + Some(SrtpProtectionProfileRef::from_ptr(profile as *mut _)) + } + } + } + + /// Returns the number of bytes remaining in the currently processed TLS record. + /// + /// If this is greater than 0, the next call to `read` will not call down to the underlying + /// stream. + /// + /// This corresponds to [`SSL_pending`]. + /// + /// [`SSL_pending`]: https://www.openssl.org/docs/man1.1.0/ssl/SSL_pending.html + pub fn pending(&self) -> usize { + unsafe { ffi::SSL_pending(self.as_ptr()) as usize } + } + + /// Returns the servername sent by the client via Server Name Indication (SNI). + /// + /// It is only useful on the server side. + /// + /// This corresponds to [`SSL_get_servername`]. + /// + /// # Note + /// + /// While the SNI specification requires that servernames be valid domain names (and therefore + /// ASCII), OpenSSL does not enforce this restriction. If the servername provided by the client + /// is not valid UTF-8, this function will return `None`. The `servername_raw` method returns + /// the raw bytes and does not have this restriction. + /// + /// [`SSL_get_servername`]: https://www.openssl.org/docs/manmaster/man3/SSL_get_servername.html + // FIXME maybe rethink in 0.11? + pub fn servername(&self, type_: NameType) -> Option<&str> { + self.servername_raw(type_) + .and_then(|b| str::from_utf8(b).ok()) + } + + /// Returns the servername sent by the client via Server Name Indication (SNI). + /// + /// It is only useful on the server side. + /// + /// This corresponds to [`SSL_get_servername`]. + /// + /// # Note + /// + /// Unlike `servername`, this method does not require the name be valid UTF-8. + /// + /// [`SSL_get_servername`]: https://www.openssl.org/docs/manmaster/man3/SSL_get_servername.html + pub fn servername_raw(&self, type_: NameType) -> Option<&[u8]> { + unsafe { + let name = ffi::SSL_get_servername(self.as_ptr(), type_.0); + if name.is_null() { + None + } else { + Some(CStr::from_ptr(name as *const _).to_bytes()) + } + } + } + + /// Changes the context corresponding to the current connection. + /// + /// It is most commonly used in the Server Name Indication (SNI) callback. + /// + /// This corresponds to `SSL_set_SSL_CTX`. + pub fn set_ssl_context(&mut self, ctx: &SslContextRef) -> Result<(), ErrorStack> { + unsafe { cvt_p(ffi::SSL_set_SSL_CTX(self.as_ptr(), ctx.as_ptr())).map(|_| ()) } + } + + /// Returns the context corresponding to the current connection. + /// + /// This corresponds to [`SSL_get_SSL_CTX`]. + /// + /// [`SSL_get_SSL_CTX`]: https://www.openssl.org/docs/man1.0.2/ssl/SSL_get_SSL_CTX.html + pub fn ssl_context(&self) -> &SslContextRef { + unsafe { + let ssl_ctx = ffi::SSL_get_SSL_CTX(self.as_ptr()); + SslContextRef::from_ptr(ssl_ctx) + } + } + + /// Returns a mutable reference to the X509 verification configuration. + /// + /// This corresponds to [`SSL_get0_param`]. + /// + /// [`SSL_get0_param`]: https://www.openssl.org/docs/man1.0.2/ssl/SSL_get0_param.html + pub fn param_mut(&mut self) -> &mut X509VerifyParamRef { + #[cfg(feature = "rpk")] + assert!( + !self.ssl_context().is_rpk(), + "This API is not supported for RPK" + ); + + unsafe { X509VerifyParamRef::from_ptr_mut(ffi::SSL_get0_param(self.as_ptr())) } + } + + /// Returns the certificate verification result. + /// + /// This corresponds to [`SSL_get_verify_result`]. + /// + /// [`SSL_get_verify_result`]: https://www.openssl.org/docs/man1.0.2/ssl/SSL_get_verify_result.html + pub fn verify_result(&self) -> X509VerifyResult { + #[cfg(feature = "rpk")] + assert!( + !self.ssl_context().is_rpk(), + "This API is not supported for RPK" + ); + + unsafe { X509VerifyError::from_raw(ffi::SSL_get_verify_result(self.as_ptr()) as c_int) } + } + + /// Returns a shared reference to the SSL session. + /// + /// This corresponds to [`SSL_get_session`]. + /// + /// [`SSL_get_session`]: https://www.openssl.org/docs/man1.1.0/ssl/SSL_get_session.html + pub fn session(&self) -> Option<&SslSessionRef> { + unsafe { + let p = ffi::SSL_get_session(self.as_ptr()); + if p.is_null() { + None + } else { + Some(SslSessionRef::from_ptr(p)) + } + } + } + + /// Copies the client_random value sent by the client in the TLS handshake into a buffer. + /// + /// Returns the number of bytes copied, or if the buffer is empty, the size of the client_random + /// value. + /// + /// This corresponds to [`SSL_get_client_random`]. + /// + /// [`SSL_get_client_random`]: https://www.openssl.org/docs/man1.1.0/ssl/SSL_get_client_random.html + pub fn client_random(&self, buf: &mut [u8]) -> usize { + unsafe { + ffi::SSL_get_client_random(self.as_ptr(), buf.as_mut_ptr() as *mut c_uchar, buf.len()) + } + } + + /// Copies the server_random value sent by the server in the TLS handshake into a buffer. + /// + /// Returns the number of bytes copied, or if the buffer is empty, the size of the server_random + /// value. + /// + /// This corresponds to [`SSL_get_server_random`]. + /// + /// [`SSL_get_server_random`]: https://www.openssl.org/docs/man1.1.0/ssl/SSL_get_client_random.html + pub fn server_random(&self, buf: &mut [u8]) -> usize { + unsafe { + ffi::SSL_get_server_random(self.as_ptr(), buf.as_mut_ptr() as *mut c_uchar, buf.len()) + } + } + + /// Derives keying material for application use in accordance to RFC 5705. + /// + /// This corresponds to [`SSL_export_keying_material`]. + /// + /// [`SSL_export_keying_material`]: https://www.openssl.org/docs/manmaster/man3/SSL_export_keying_material.html + pub fn export_keying_material( + &self, + out: &mut [u8], + label: &str, + context: Option<&[u8]>, + ) -> Result<(), ErrorStack> { + unsafe { + let (context, contextlen, use_context) = match context { + Some(context) => (context.as_ptr() as *const c_uchar, context.len(), 1), + None => (ptr::null(), 0, 0), + }; + cvt(ffi::SSL_export_keying_material( + self.as_ptr(), + out.as_mut_ptr() as *mut c_uchar, + out.len(), + label.as_ptr() as *const c_char, + label.len(), + context, + contextlen, + use_context, + )) + .map(|_| ()) + } + } + + /// Sets the session to be used. + /// + /// This should be called before the handshake to attempt to reuse a previously established + /// session. If the server is not willing to reuse the session, a new one will be transparently + /// negotiated. + /// + /// This corresponds to [`SSL_set_session`]. + /// + /// # Safety + /// + /// The caller of this method is responsible for ensuring that the session is associated + /// with the same `SslContext` as this `Ssl`. + /// + /// [`SSL_set_session`]: https://www.openssl.org/docs/manmaster/man3/SSL_set_session.html + pub unsafe fn set_session(&mut self, session: &SslSessionRef) -> Result<(), ErrorStack> { + cvt(ffi::SSL_set_session(self.as_ptr(), session.as_ptr())).map(|_| ()) + } + + /// Determines if the session provided to `set_session` was successfully reused. + /// + /// This corresponds to [`SSL_session_reused`]. + /// + /// [`SSL_session_reused`]: https://www.openssl.org/docs/man1.1.0/ssl/SSL_session_reused.html + pub fn session_reused(&self) -> bool { + unsafe { ffi::SSL_session_reused(self.as_ptr()) != 0 } + } + + /// Sets the status response a client wishes the server to reply with. + /// + /// This corresponds to [`SSL_set_tlsext_status_type`]. + /// + /// [`SSL_set_tlsext_status_type`]: https://www.openssl.org/docs/man1.0.2/ssl/SSL_set_tlsext_status_type.html + pub fn set_status_type(&mut self, type_: StatusType) -> Result<(), ErrorStack> { + unsafe { + cvt(ffi::SSL_set_tlsext_status_type(self.as_ptr(), type_.as_raw()) as c_int).map(|_| ()) + } + } + + /// Returns the server's OCSP response, if present. + /// + /// This corresponds to [`SSL_get_tlsext_status_ocsp_resp`]. + /// + /// [`SSL_get_tlsext_status_ocsp_resp`]: https://www.openssl.org/docs/man1.0.2/ssl/SSL_set_tlsext_status_type.html + pub fn ocsp_status(&self) -> Option<&[u8]> { + unsafe { + let mut p = ptr::null(); + let len = ffi::SSL_get_tlsext_status_ocsp_resp(self.as_ptr(), &mut p); + + if len == 0 { + None + } else { + Some(slice::from_raw_parts(p, len)) + } + } + } + + /// Sets the OCSP response to be returned to the client. + /// + /// This corresponds to [`SSL_set_tlsext_status_ocsp_resp`]. + /// + /// [`SSL_set_tlsext_status_ocsp_resp`]: https://www.openssl.org/docs/man1.0.2/ssl/SSL_set_tlsext_status_type.html + pub fn set_ocsp_status(&mut self, response: &[u8]) -> Result<(), ErrorStack> { + unsafe { + assert!(response.len() <= c_int::max_value() as usize); + let p = cvt_p(ffi::OPENSSL_malloc(response.len() as _))?; + ptr::copy_nonoverlapping(response.as_ptr(), p as *mut u8, response.len()); + cvt(ffi::SSL_set_tlsext_status_ocsp_resp( + self.as_ptr(), + p as *mut c_uchar, + response.len(), + ) as c_int) + .map(|_| ()) + } + } + + /// Determines if this `Ssl` is configured for server-side or client-side use. + /// + /// This corresponds to [`SSL_is_server`]. + /// + /// [`SSL_is_server`]: https://www.openssl.org/docs/manmaster/man3/SSL_is_server.html + pub fn is_server(&self) -> bool { + unsafe { SSL_is_server(self.as_ptr()) != 0 } + } + + /// Sets the extra data at the specified index. + /// + /// This can be used to provide data to callbacks registered with the context. Use the + /// `Ssl::new_ex_index` method to create an `Index`. + /// + /// This corresponds to [`SSL_set_ex_data`]. + /// + /// [`SSL_set_ex_data`]: https://www.openssl.org/docs/manmaster/man3/SSL_set_ex_data.html + pub fn set_ex_data(&mut self, index: Index, data: T) { + unsafe { + let data = Box::new(data); + ffi::SSL_set_ex_data( + self.as_ptr(), + index.as_raw(), + Box::into_raw(data) as *mut c_void, + ); + } + } + + /// Returns a reference to the extra data at the specified index. + /// + /// This corresponds to [`SSL_get_ex_data`]. + /// + /// [`SSL_get_ex_data`]: https://www.openssl.org/docs/manmaster/man3/SSL_set_ex_data.html + pub fn ex_data(&self, index: Index) -> Option<&T> { + unsafe { + let data = ffi::SSL_get_ex_data(self.as_ptr(), index.as_raw()); + if data.is_null() { + None + } else { + Some(&*(data as *const T)) + } + } + } + + /// Returns a mutable reference to the extra data at the specified index. + /// + /// This corresponds to [`SSL_get_ex_data`]. + /// + /// [`SSL_get_ex_data`]: https://www.openssl.org/docs/manmaster/man3/SSL_set_ex_data.html + pub fn ex_data_mut(&mut self, index: Index) -> Option<&mut T> { + unsafe { + let data = ffi::SSL_get_ex_data(self.as_ptr(), index.as_raw()); + if data.is_null() { + None + } else { + Some(&mut *(data as *mut T)) + } + } + } + + /// Copies the contents of the last Finished message sent to the peer into the provided buffer. + /// + /// The total size of the message is returned, so this can be used to determine the size of the + /// buffer required. + /// + /// This corresponds to `SSL_get_finished`. + pub fn finished(&self, buf: &mut [u8]) -> usize { + unsafe { ffi::SSL_get_finished(self.as_ptr(), buf.as_mut_ptr() as *mut c_void, buf.len()) } + } + + /// Copies the contents of the last Finished message received from the peer into the provided + /// buffer. + /// + /// The total size of the message is returned, so this can be used to determine the size of the + /// buffer required. + /// + /// This corresponds to `SSL_get_peer_finished`. + pub fn peer_finished(&self, buf: &mut [u8]) -> usize { + unsafe { + ffi::SSL_get_peer_finished(self.as_ptr(), buf.as_mut_ptr() as *mut c_void, buf.len()) + } + } + + /// Determines if the initial handshake has been completed. + /// + /// This corresponds to [`SSL_is_init_finished`]. + /// + /// [`SSL_is_init_finished`]: https://www.openssl.org/docs/man1.1.1/man3/SSL_is_init_finished.html + pub fn is_init_finished(&self) -> bool { + unsafe { ffi::SSL_is_init_finished(self.as_ptr()) != 0 } + } + + /// Sets the MTU used for DTLS connections. + /// + /// This corresponds to `SSL_set_mtu`. + pub fn set_mtu(&mut self, mtu: u32) -> Result<(), ErrorStack> { + unsafe { cvt(ffi::SSL_set_mtu(self.as_ptr(), mtu as c_uint) as c_int).map(|_| ()) } + } + + /// Sets the certificate. + /// + /// This corresponds to [`SSL_use_certificate`]. + /// + /// [`SSL_use_certificate`]: https://www.openssl.org/docs/man1.1.1/man3/SSL_use_certificate.html + pub fn set_certificate(&mut self, cert: &X509Ref) -> Result<(), ErrorStack> { + unsafe { + cvt(ffi::SSL_use_certificate(self.as_ptr(), cert.as_ptr()))?; + } + + Ok(()) + } +} + +/// An SSL stream midway through the handshake process. +#[derive(Debug)] +pub struct MidHandshakeSslStream { + stream: SslStream, + error: Error, +} + +impl MidHandshakeSslStream { + /// Returns a shared reference to the inner stream. + pub fn get_ref(&self) -> &S { + self.stream.get_ref() + } + + /// Returns a mutable reference to the inner stream. + pub fn get_mut(&mut self) -> &mut S { + self.stream.get_mut() + } + + /// Returns a shared reference to the `Ssl` of the stream. + pub fn ssl(&self) -> &SslRef { + self.stream.ssl() + } + + /// Returns a mutable reference to the `Ssl` of the stream. + pub fn ssl_mut(&mut self) -> &mut SslRef { + self.stream.ssl_mut() + } + + /// Returns the underlying error which interrupted this handshake. + pub fn error(&self) -> &Error { + &self.error + } + + /// Consumes `self`, returning its error. + pub fn into_error(self) -> Error { + self.error + } + + /// Returns the source data stream. + pub fn into_source_stream(self) -> S { + self.stream.into_inner() + } + + /// Returns both the error and the source data stream, consuming `self`. + pub fn into_parts(self) -> (Error, S) { + (self.error, self.stream.into_inner()) + } + + /// Restarts the handshake process. + /// + /// This corresponds to [`SSL_do_handshake`]. + /// + /// [`SSL_do_handshake`]: https://www.openssl.org/docs/manmaster/man3/SSL_do_handshake.html + pub fn handshake(mut self) -> Result, HandshakeError> { + let ret = unsafe { ffi::SSL_do_handshake(self.stream.ssl.as_ptr()) }; + if ret > 0 { + Ok(self.stream) + } else { + self.error = self.stream.make_error(ret); + match self.error.would_block() { + true => Err(HandshakeError::WouldBlock(self)), + false => Err(HandshakeError::Failure(self)), + } + } + } +} + +/// A TLS session over a stream. +pub struct SslStream { + ssl: ManuallyDrop, + method: ManuallyDrop, + _p: PhantomData, +} + +impl Drop for SslStream { + fn drop(&mut self) { + // ssl holds a reference to method internally so it has to drop first + unsafe { + ManuallyDrop::drop(&mut self.ssl); + ManuallyDrop::drop(&mut self.method); + } + } +} + +impl fmt::Debug for SslStream +where + S: fmt::Debug, +{ + fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { + fmt.debug_struct("SslStream") + .field("stream", &self.get_ref()) + .field("ssl", &self.ssl()) + .finish() + } +} + +impl SslStream { + fn new_base(ssl: Ssl, stream: S) -> Self { + unsafe { + let (bio, method) = bio::new(stream).unwrap(); + ffi::SSL_set_bio(ssl.as_ptr(), bio, bio); + + SslStream { + ssl: ManuallyDrop::new(ssl), + method: ManuallyDrop::new(method), + _p: PhantomData, + } + } + } + + /// Creates a new `SslStream`. + /// + /// This function performs no IO; the stream will not have performed any part of the handshake + /// with the peer. The `connect` and `accept` methods can be used to + /// explicitly perform the handshake. + pub fn new(ssl: Ssl, stream: S) -> Result { + Ok(Self::new_base(ssl, stream)) + } + + /// Constructs an `SslStream` from a pointer to the underlying OpenSSL `SSL` struct. + /// + /// This is useful if the handshake has already been completed elsewhere. + /// + /// # Safety + /// + /// The caller must ensure the pointer is valid. + pub unsafe fn from_raw_parts(ssl: *mut ffi::SSL, stream: S) -> Self { + let ssl = Ssl::from_ptr(ssl); + Self::new_base(ssl, stream) + } + + /// Like `read`, but returns an `ssl::Error` rather than an `io::Error`. + /// + /// It is particularly useful with a nonblocking socket, where the error value will identify if + /// OpenSSL is waiting on read or write readiness. + /// + /// This corresponds to [`SSL_read`]. + /// + /// [`SSL_read`]: https://www.openssl.org/docs/manmaster/man3/SSL_read.html + pub fn ssl_read(&mut self, buf: &mut [u8]) -> Result { + // The intepretation of the return code here is a little odd with a + // zero-length write. OpenSSL will likely correctly report back to us + // that it read zero bytes, but zero is also the sentinel for "error". + // To avoid that confusion short-circuit that logic and return quickly + // if `buf` has a length of zero. + if buf.is_empty() { + return Ok(0); + } + + let ret = self.ssl.read(buf); + if ret > 0 { + Ok(ret as usize) + } else { + Err(self.make_error(ret)) + } + } + + /// Like `write`, but returns an `ssl::Error` rather than an `io::Error`. + /// + /// It is particularly useful with a nonblocking socket, where the error value will identify if + /// OpenSSL is waiting on read or write readiness. + /// + /// This corresponds to [`SSL_write`]. + /// + /// [`SSL_write`]: https://www.openssl.org/docs/manmaster/man3/SSL_write.html + pub fn ssl_write(&mut self, buf: &[u8]) -> Result { + // See above for why we short-circuit on zero-length buffers + if buf.is_empty() { + return Ok(0); + } + + let ret = self.ssl.write(buf); + if ret > 0 { + Ok(ret as usize) + } else { + Err(self.make_error(ret)) + } + } + + /// Shuts down the session. + /// + /// The shutdown process consists of two steps. The first step sends a close notify message to + /// the peer, after which `ShutdownResult::Sent` is returned. The second step awaits the receipt + /// of a close notify message from the peer, after which `ShutdownResult::Received` is returned. + /// + /// While the connection may be closed after the first step, it is recommended to fully shut the + /// session down. In particular, it must be fully shut down if the connection is to be used for + /// further communication in the future. + /// + /// This corresponds to [`SSL_shutdown`]. + /// + /// [`SSL_shutdown`]: https://www.openssl.org/docs/man1.0.2/ssl/SSL_shutdown.html + pub fn shutdown(&mut self) -> Result { + match unsafe { ffi::SSL_shutdown(self.ssl.as_ptr()) } { + 0 => Ok(ShutdownResult::Sent), + 1 => Ok(ShutdownResult::Received), + n => Err(self.make_error(n)), + } + } + + /// Returns the session's shutdown state. + /// + /// This corresponds to [`SSL_get_shutdown`]. + /// + /// [`SSL_get_shutdown`]: https://www.openssl.org/docs/man1.1.1/man3/SSL_set_shutdown.html + pub fn get_shutdown(&mut self) -> ShutdownState { + unsafe { + let bits = ffi::SSL_get_shutdown(self.ssl.as_ptr()); + ShutdownState::from_bits_retain(bits) + } + } + + /// Sets the session's shutdown state. + /// + /// This can be used to tell OpenSSL that the session should be cached even if a full two-way + /// shutdown was not completed. + /// + /// This corresponds to [`SSL_set_shutdown`]. + /// + /// [`SSL_set_shutdown`]: https://www.openssl.org/docs/man1.1.1/man3/SSL_set_shutdown.html + pub fn set_shutdown(&mut self, state: ShutdownState) { + unsafe { ffi::SSL_set_shutdown(self.ssl.as_ptr(), state.bits()) } + } + + /// Initiates a client-side TLS handshake. + /// + /// This corresponds to [`SSL_connect`]. + /// + /// [`SSL_connect`]: https://www.openssl.org/docs/man1.1.1/man3/SSL_connect.html + pub fn connect(&mut self) -> Result<(), Error> { + let ret = unsafe { ffi::SSL_connect(self.ssl.as_ptr()) }; + if ret > 0 { + Ok(()) + } else { + Err(self.make_error(ret)) + } + } + + /// Initiates a server-side TLS handshake. + /// + /// This corresponds to [`SSL_accept`]. + /// + /// [`SSL_accept`]: https://www.openssl.org/docs/man1.1.1/man3/SSL_accept.html + pub fn accept(&mut self) -> Result<(), Error> { + let ret = unsafe { ffi::SSL_accept(self.ssl.as_ptr()) }; + if ret > 0 { + Ok(()) + } else { + Err(self.make_error(ret)) + } + } + + /// Initiates the handshake. + /// + /// This corresponds to [`SSL_do_handshake`]. + /// + /// [`SSL_do_handshake`]: https://www.openssl.org/docs/man1.1.1/man3/SSL_do_handshake.html + pub fn do_handshake(&mut self) -> Result<(), Error> { + let ret = unsafe { ffi::SSL_do_handshake(self.ssl.as_ptr()) }; + if ret > 0 { + Ok(()) + } else { + Err(self.make_error(ret)) + } + } +} + +impl SslStream { + fn make_error(&mut self, ret: c_int) -> Error { + self.check_panic(); + + let code = self.ssl.get_error(ret); + + let cause = match code { + ErrorCode::SSL => Some(InnerError::Ssl(ErrorStack::get())), + ErrorCode::SYSCALL => { + let errs = ErrorStack::get(); + if errs.errors().is_empty() { + self.get_bio_error().map(InnerError::Io) + } else { + Some(InnerError::Ssl(errs)) + } + } + ErrorCode::ZERO_RETURN => None, + ErrorCode::WANT_READ | ErrorCode::WANT_WRITE => { + self.get_bio_error().map(InnerError::Io) + } + _ => None, + }; + + Error { code, cause } + } + + fn check_panic(&mut self) { + if let Some(err) = unsafe { bio::take_panic::(self.ssl.get_raw_rbio()) } { + resume_unwind(err) + } + } + + fn get_bio_error(&mut self) -> Option { + unsafe { bio::take_error::(self.ssl.get_raw_rbio()) } + } + + /// Converts the SslStream to the underlying data stream. + pub fn into_inner(self) -> S { + unsafe { bio::take_stream::(self.ssl.get_raw_rbio()) } + } + + /// Returns a shared reference to the underlying stream. + pub fn get_ref(&self) -> &S { + unsafe { + let bio = self.ssl.get_raw_rbio(); + bio::get_ref(bio) + } + } + + /// Returns a mutable reference to the underlying stream. + /// + /// # Warning + /// + /// It is inadvisable to read from or write to the underlying stream as it + /// will most likely corrupt the SSL session. + pub fn get_mut(&mut self) -> &mut S { + unsafe { + let bio = self.ssl.get_raw_rbio(); + bio::get_mut(bio) + } + } + + /// Returns a shared reference to the `Ssl` object associated with this stream. + pub fn ssl(&self) -> &SslRef { + &self.ssl + } + + /// Returns a mutable reference to the `Ssl` object associated with this stream. + pub fn ssl_mut(&mut self) -> &mut SslRef { + &mut self.ssl + } +} + +impl Read for SslStream { + fn read(&mut self, buf: &mut [u8]) -> io::Result { + loop { + match self.ssl_read(buf) { + Ok(n) => return Ok(n), + Err(ref e) if e.code() == ErrorCode::ZERO_RETURN => return Ok(0), + Err(ref e) if e.code() == ErrorCode::SYSCALL && e.io_error().is_none() => { + return Ok(0); + } + Err(ref e) if e.code() == ErrorCode::WANT_READ && e.io_error().is_none() => {} + Err(e) => { + return Err(e + .into_io_error() + .unwrap_or_else(|e| io::Error::new(io::ErrorKind::Other, e))); + } + } + } + } +} + +impl Write for SslStream { + fn write(&mut self, buf: &[u8]) -> io::Result { + loop { + match self.ssl_write(buf) { + Ok(n) => return Ok(n), + Err(ref e) if e.code() == ErrorCode::WANT_READ && e.io_error().is_none() => {} + Err(e) => { + return Err(e + .into_io_error() + .unwrap_or_else(|e| io::Error::new(io::ErrorKind::Other, e))); + } + } + } + } + + fn flush(&mut self) -> io::Result<()> { + self.get_mut().flush() + } +} + +/// A partially constructed `SslStream`, useful for unusual handshakes. +pub struct SslStreamBuilder { + inner: SslStream, +} + +impl SslStreamBuilder +where + S: Read + Write, +{ + /// Begin creating an `SslStream` atop `stream` + pub fn new(ssl: Ssl, stream: S) -> Self { + Self { + inner: SslStream::new_base(ssl, stream), + } + } + + /// Configure as an outgoing stream from a client. + /// + /// This corresponds to [`SSL_set_connect_state`]. + /// + /// [`SSL_set_connect_state`]: https://www.openssl.org/docs/manmaster/man3/SSL_set_connect_state.html + pub fn set_connect_state(&mut self) { + unsafe { ffi::SSL_set_connect_state(self.inner.ssl.as_ptr()) } + } + + /// Configure as an incoming stream to a server. + /// + /// This corresponds to [`SSL_set_accept_state`]. + /// + /// [`SSL_set_accept_state`]: https://www.openssl.org/docs/manmaster/man3/SSL_set_accept_state.html + pub fn set_accept_state(&mut self) { + unsafe { ffi::SSL_set_accept_state(self.inner.ssl.as_ptr()) } + } + + /// Initiates a client-side TLS handshake, returning a [`MidHandshakeSslStream`]. + /// + /// This method calls [`Self::set_connect_state`] and returns without actually + /// initiating the handshake. The caller is then free to call + /// [`MidHandshakeSslStream`] and loop on [`HandshakeError::WouldBlock`]. + pub fn setup_connect(mut self) -> MidHandshakeSslStream { + self.set_connect_state(); + + #[cfg(feature = "kx-safe-default")] + self.inner.ssl.client_set_default_curves_list(); + + MidHandshakeSslStream { + stream: self.inner, + error: Error { + code: ErrorCode::WANT_WRITE, + cause: Some(InnerError::Io(io::Error::new( + io::ErrorKind::WouldBlock, + "connect handshake has not started yet", + ))), + }, + } + } + + /// Attempts a client-side TLS handshake. + /// + /// This is a convenience method which combines [`Self::setup_connect`] and + /// [`MidHandshakeSslStream::handshake`]. + pub fn connect(self) -> Result, HandshakeError> { + self.setup_connect().handshake() + } + + /// Initiates a server-side TLS handshake, returning a [`MidHandshakeSslStream`]. + /// + /// This method calls [`Self::set_accept_state`] and returns without actually + /// initiating the handshake. The caller is then free to call + /// [`MidHandshakeSslStream`] and loop on [`HandshakeError::WouldBlock`]. + pub fn setup_accept(mut self) -> MidHandshakeSslStream { + self.set_accept_state(); + + #[cfg(feature = "kx-safe-default")] + self.inner.ssl.server_set_default_curves_list(); + + MidHandshakeSslStream { + stream: self.inner, + error: Error { + code: ErrorCode::WANT_READ, + cause: Some(InnerError::Io(io::Error::new( + io::ErrorKind::WouldBlock, + "accept handshake has not started yet", + ))), + }, + } + } + + /// Attempts a server-side TLS handshake. + /// + /// This is a convenience method which combines [`Self::setup_accept`] and + /// [`MidHandshakeSslStream::handshake`]. + pub fn accept(self) -> Result, HandshakeError> { + self.setup_accept().handshake() + } + + /// Initiates the handshake. + /// + /// This will fail if `set_accept_state` or `set_connect_state` was not called first. + /// + /// This corresponds to [`SSL_do_handshake`]. + /// + /// [`SSL_do_handshake`]: https://www.openssl.org/docs/manmaster/man3/SSL_do_handshake.html + pub fn handshake(self) -> Result, HandshakeError> { + let mut stream = self.inner; + let ret = unsafe { ffi::SSL_do_handshake(stream.ssl.as_ptr()) }; + if ret > 0 { + Ok(stream) + } else { + let error = stream.make_error(ret); + match error.would_block() { + true => Err(HandshakeError::WouldBlock(MidHandshakeSslStream { + stream, + error, + })), + false => Err(HandshakeError::Failure(MidHandshakeSslStream { + stream, + error, + })), + } + } + } +} + +impl SslStreamBuilder { + /// Returns a shared reference to the underlying stream. + pub fn get_ref(&self) -> &S { + unsafe { + let bio = self.inner.ssl.get_raw_rbio(); + bio::get_ref(bio) + } + } + + /// Returns a mutable reference to the underlying stream. + /// + /// # Warning + /// + /// It is inadvisable to read from or write to the underlying stream as it + /// will most likely corrupt the SSL session. + pub fn get_mut(&mut self) -> &mut S { + unsafe { + let bio = self.inner.ssl.get_raw_rbio(); + bio::get_mut(bio) + } + } + + /// Returns a shared reference to the `Ssl` object associated with this builder. + pub fn ssl(&self) -> &SslRef { + &self.inner.ssl + } + + /// Set the DTLS MTU size. + /// + /// It will be ignored if the value is smaller than the minimum packet size + /// the DTLS protocol requires. + /// + /// # Panics + /// This function panics if the given mtu size can't be represented in a positive `c_long` range + #[deprecated(note = "Use SslRef::set_mtu instead", since = "0.10.30")] + pub fn set_dtls_mtu_size(&mut self, mtu_size: usize) { + unsafe { + let bio = self.inner.ssl.get_raw_rbio(); + bio::set_dtls_mtu_size::(bio, mtu_size); + } + } +} + +/// The result of a shutdown request. +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +pub enum ShutdownResult { + /// A close notify message has been sent to the peer. + Sent, + + /// A close notify response message has been received from the peer. + Received, +} + +bitflags! { + /// The shutdown state of a session. + #[derive(Debug, PartialEq, Eq, Clone, Copy, PartialOrd, Ord, Hash)] + pub struct ShutdownState: c_int { + /// A close notify message has been sent to the peer. + const SENT = ffi::SSL_SENT_SHUTDOWN; + /// A close notify message has been received from the peer. + const RECEIVED = ffi::SSL_RECEIVED_SHUTDOWN; + } +} + +/// Describes private key hooks. This is used to off-load signing operations to +/// a custom, potentially asynchronous, backend. Metadata about the key such as +/// the type and size are parsed out of the certificate. +/// +/// Corresponds to [`ssl_private_key_method_st`]. +/// +/// [`ssl_private_key_method_st`]: https://commondatastorage.googleapis.com/chromium-boringssl-docs/ssl.h.html#ssl_private_key_method_st +pub trait PrivateKeyMethod: Send + Sync + 'static { + /// Signs the message `input` using the specified signature algorithm. + /// + /// On success, it returns `Ok(written)` where `written` is the number of + /// bytes written into `output`. On failure, it returns + /// `Err(PrivateKeyMethodError::FAILURE)`. If the operation has not completed, + /// it returns `Err(PrivateKeyMethodError::RETRY)`. + /// + /// The caller should arrange for the high-level operation on `ssl` to be + /// retried when the operation is completed. This will result in a call to + /// [`Self::complete`]. + fn sign( + &self, + ssl: &mut SslRef, + input: &[u8], + signature_algorithm: SslSignatureAlgorithm, + output: &mut [u8], + ) -> Result; + + /// Decrypts `input`. + /// + /// On success, it returns `Ok(written)` where `written` is the number of + /// bytes written into `output`. On failure, it returns + /// `Err(PrivateKeyMethodError::FAILURE)`. If the operation has not completed, + /// it returns `Err(PrivateKeyMethodError::RETRY)`. + /// + /// The caller should arrange for the high-level operation on `ssl` to be + /// retried when the operation is completed. This will result in a call to + /// [`Self::complete`]. + /// + /// This method only works with RSA keys and should perform a raw RSA + /// decryption operation with no padding. + // NOTE(nox): What does it mean that it is an error? + fn decrypt( + &self, + ssl: &mut SslRef, + input: &[u8], + output: &mut [u8], + ) -> Result; + + /// Completes a pending operation. + /// + /// On success, it returns `Ok(written)` where `written` is the number of + /// bytes written into `output`. On failure, it returns + /// `Err(PrivateKeyMethodError::FAILURE)`. If the operation has not completed, + /// it returns `Err(PrivateKeyMethodError::RETRY)`. + /// + /// This method may be called arbitrarily many times before completion. + fn complete(&self, ssl: &mut SslRef, output: &mut [u8]) + -> Result; +} + +/// An error returned from a private key method. +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +pub struct PrivateKeyMethodError(ffi::ssl_private_key_result_t); + +impl PrivateKeyMethodError { + /// A fatal error occured and the handshake should be terminated. + pub const FAILURE: Self = Self(ffi::ssl_private_key_result_t::ssl_private_key_failure); + + /// The operation could not be completed and should be retried later. + pub const RETRY: Self = Self(ffi::ssl_private_key_result_t::ssl_private_key_retry); +} + +use crate::ffi::{SSL_CTX_up_ref, SSL_SESSION_get_master_key, SSL_SESSION_up_ref, SSL_is_server}; + +use crate::ffi::{DTLS_method, TLS_client_method, TLS_method, TLS_server_method}; + +use std::sync::Once; + +unsafe fn get_new_idx(f: ffi::CRYPTO_EX_free) -> c_int { + // hack around https://rt.openssl.org/Ticket/Display.html?id=3710&user=guest&pass=guest + static ONCE: Once = Once::new(); + ONCE.call_once(|| { + ffi::SSL_CTX_get_ex_new_index(0, ptr::null_mut(), ptr::null_mut(), None, None); + }); + + ffi::SSL_CTX_get_ex_new_index(0, ptr::null_mut(), ptr::null_mut(), None, f) +} + +unsafe fn get_new_ssl_idx(f: ffi::CRYPTO_EX_free) -> c_int { + // hack around https://rt.openssl.org/Ticket/Display.html?id=3710&user=guest&pass=guest + static ONCE: Once = Once::new(); + ONCE.call_once(|| { + ffi::SSL_get_ex_new_index(0, ptr::null_mut(), ptr::null_mut(), None, None); + }); + + ffi::SSL_get_ex_new_index(0, ptr::null_mut(), ptr::null_mut(), None, f) +} diff --git a/crates/boring/src/ssl/test/mod.rs b/crates/boring/src/ssl/test/mod.rs new file mode 100644 index 000000000..d3319f5f6 --- /dev/null +++ b/crates/boring/src/ssl/test/mod.rs @@ -0,0 +1,1046 @@ +use hex; +use std::io; +use std::io::prelude::*; +use std::mem; +use std::net::{TcpListener, TcpStream}; +use std::path::Path; +use std::sync::atomic::{AtomicBool, Ordering}; +use std::thread; + +use crate::error::ErrorStack; +use crate::hash::MessageDigest; +use crate::pkey::PKey; +use crate::srtp::SrtpProfileId; +use crate::ssl; +use crate::ssl::test::server::Server; +use crate::ssl::SslVersion; +use crate::ssl::{ + ExtensionType, ShutdownResult, ShutdownState, Ssl, SslAcceptor, SslAcceptorBuilder, + SslConnector, SslContext, SslFiletype, SslMethod, SslOptions, SslStream, SslVerifyMode, +}; +use crate::x509::store::X509StoreBuilder; +use crate::x509::verify::X509CheckFlags; +use crate::x509::{X509Name, X509}; + +mod private_key_method; +mod server; +mod session; + +static ROOT_CERT: &[u8] = include_bytes!("../../../test/root-ca.pem"); +static CERT: &[u8] = include_bytes!("../../../test/cert.pem"); +static KEY: &[u8] = include_bytes!("../../../test/key.pem"); + +#[test] +fn verify_untrusted() { + let mut server = Server::builder(); + server.should_error(); + let server = server.build(); + + let mut client = server.client(); + client.ctx().set_verify(SslVerifyMode::PEER); + + client.connect_err(); +} + +#[test] +fn verify_trusted() { + let server = Server::builder().build(); + let client = server.client_with_root_ca(); + + client.connect(); +} + +#[test] +fn verify_trusted_with_set_cert() { + let server = Server::builder().build(); + + let mut store = X509StoreBuilder::new().unwrap(); + let x509 = X509::from_pem(ROOT_CERT).unwrap(); + store.add_cert(x509).unwrap(); + + let mut client = server.client(); + client.ctx().set_verify(SslVerifyMode::PEER); + client.ctx().set_verify_cert_store(store.build()).unwrap(); + + client.connect(); +} + +#[test] +fn verify_untrusted_callback_override_ok() { + let server = Server::builder().build(); + + let mut client = server.client(); + client + .ctx() + .set_verify_callback(SslVerifyMode::PEER, |_, x509| { + assert!(x509.current_cert().is_some()); + true + }); + + client.connect(); +} + +#[test] +fn verify_untrusted_callback_override_bad() { + let mut server = Server::builder(); + server.should_error(); + let server = server.build(); + + let mut client = server.client(); + client + .ctx() + .set_verify_callback(SslVerifyMode::PEER, |_, _| false); + + client.connect_err(); +} + +#[test] +fn verify_trusted_callback_override_ok() { + let server = Server::builder().build(); + let mut client = server.client_with_root_ca(); + + client + .ctx() + .set_verify_callback(SslVerifyMode::PEER, |_, x509| { + assert!(x509.current_cert().is_some()); + true + }); + + client.connect(); +} + +#[test] +fn verify_trusted_callback_override_bad() { + let mut server = Server::builder(); + + server.should_error(); + + let server = server.build(); + let mut client = server.client_with_root_ca(); + + client + .ctx() + .set_verify_callback(SslVerifyMode::PEER, |_, _| false); + + client.connect_err(); +} + +#[test] +fn verify_callback_load_certs() { + let server = Server::builder().build(); + + let mut client = server.client(); + client + .ctx() + .set_verify_callback(SslVerifyMode::PEER, |_, x509| { + assert!(x509.current_cert().is_some()); + true + }); + + client.connect(); +} + +#[test] +fn verify_trusted_get_error_ok() { + let server = Server::builder().build(); + let mut client = server.client_with_root_ca(); + + client + .ctx() + .set_verify_callback(SslVerifyMode::PEER, |_, x509| { + assert_eq!(x509.verify_result(), Ok(())); + true + }); + + client.connect(); +} + +#[test] +fn verify_trusted_get_error_err() { + let mut server = Server::builder(); + server.should_error(); + let server = server.build(); + + let mut client = server.client(); + client + .ctx() + .set_verify_callback(SslVerifyMode::PEER, |_, x509| { + assert!(x509.verify_result().is_err()); + false + }); + + client.connect_err(); +} + +#[test] +fn verify_callback() { + static CALLED_BACK: AtomicBool = AtomicBool::new(false); + + let server = Server::builder().build(); + + let mut client = server.client(); + let expected = "59172d9313e84459bcff27f967e79e6e9217e584"; + client + .ctx() + .set_verify_callback(SslVerifyMode::PEER, move |_, x509| { + CALLED_BACK.store(true, Ordering::SeqCst); + let cert = x509.current_cert().unwrap(); + let digest = cert.digest(MessageDigest::sha1()).unwrap(); + assert_eq!(hex::encode(digest), expected); + true + }); + + client.connect(); + assert!(CALLED_BACK.load(Ordering::SeqCst)); +} + +#[test] +fn ssl_verify_callback() { + static CALLED_BACK: AtomicBool = AtomicBool::new(false); + + let server = Server::builder().build(); + + let mut client = server.client().build().builder(); + let expected = "59172d9313e84459bcff27f967e79e6e9217e584"; + client + .ssl() + .set_verify_callback(SslVerifyMode::PEER, move |_, x509| { + CALLED_BACK.store(true, Ordering::SeqCst); + let cert = x509.current_cert().unwrap(); + let digest = cert.digest(MessageDigest::sha1()).unwrap(); + assert_eq!(hex::encode(digest), expected); + true + }); + + client.connect(); + assert!(CALLED_BACK.load(Ordering::SeqCst)); +} + +#[test] +fn get_ctx_options() { + let ctx = SslContext::builder(SslMethod::tls()).unwrap(); + ctx.options(); +} + +#[test] +fn set_ctx_options() { + let mut ctx = SslContext::builder(SslMethod::tls()).unwrap(); + let opts = ctx.set_options(SslOptions::NO_TICKET); + assert!(opts.contains(SslOptions::NO_TICKET)); +} + +#[test] +fn clear_ctx_options() { + let mut ctx = SslContext::builder(SslMethod::tls()).unwrap(); + ctx.set_options(SslOptions::NO_TICKET); + let opts = ctx.clear_options(SslOptions::NO_TICKET); + assert!(!opts.contains(SslOptions::NO_TICKET)); +} + +#[test] +fn zero_length_buffers() { + let server = Server::builder().build(); + + let mut s = server.client().connect(); + assert_eq!(s.write(&[]).unwrap(), 0); + assert_eq!(s.read(&mut []).unwrap(), 0); +} + +#[test] +fn peer_certificate() { + let server = Server::builder().build(); + + let s = server.client().connect(); + let cert = s.ssl().peer_certificate().unwrap(); + let fingerprint = cert.digest(MessageDigest::sha1()).unwrap(); + assert_eq!( + hex::encode(fingerprint), + "59172d9313e84459bcff27f967e79e6e9217e584" + ); +} + +#[test] +fn pending() { + let mut server = Server::builder(); + server.io_cb(|mut s| s.write_all(&[0; 10]).unwrap()); + let server = server.build(); + + let mut s = server.client().connect(); + s.read_exact(&mut [0]).unwrap(); + + assert_eq!(s.ssl().pending(), 9); + assert_eq!(s.read(&mut [0; 10]).unwrap(), 9); +} + +#[test] +fn state() { + let server = Server::builder().build(); + + let s = server.client().connect(); + // NOTE: Boring returs a placeholder string for state_string + assert_eq!(s.ssl().state_string(), "!!!!!!"); + assert_eq!( + s.ssl().state_string_long(), + "SSL negotiation finished successfully" + ); +} + +/// Tests that when both the client as well as the server use SRTP and their +/// lists of supported protocols have an overlap -- with only ONE protocol +/// being valid for both. +#[test] +fn test_connect_with_srtp_ctx() { + let listener = TcpListener::bind("127.0.0.1:0").unwrap(); + let addr = listener.local_addr().unwrap(); + + let guard = thread::spawn(move || { + let stream = listener.accept().unwrap().0; + let mut ctx = SslContext::builder(SslMethod::dtls()).unwrap(); + ctx.set_tlsext_use_srtp("SRTP_AES128_CM_SHA1_80:SRTP_AES128_CM_SHA1_32") + .unwrap(); + ctx.set_certificate_file(Path::new("test/cert.pem"), SslFiletype::PEM) + .unwrap(); + ctx.set_private_key_file(Path::new("test/key.pem"), SslFiletype::PEM) + .unwrap(); + let mut ssl = Ssl::new(&ctx.build()).unwrap(); + ssl.set_mtu(1500).unwrap(); + let mut stream = ssl.accept(stream).unwrap(); + + let mut buf = [0; 60]; + stream + .ssl() + .export_keying_material(&mut buf, "EXTRACTOR-dtls_srtp", None) + .unwrap(); + + stream.write_all(&[0]).unwrap(); + + buf + }); + + let stream = TcpStream::connect(addr).unwrap(); + let mut ctx = SslContext::builder(SslMethod::dtls()).unwrap(); + ctx.set_tlsext_use_srtp("SRTP_AES128_CM_SHA1_80:SRTP_AES128_CM_SHA1_32") + .unwrap(); + let mut ssl = Ssl::new(&ctx.build()).unwrap(); + ssl.set_mtu(1500).unwrap(); + let mut stream = ssl.connect(stream).unwrap(); + + let mut buf = [1; 60]; + { + let srtp_profile = stream.ssl().selected_srtp_profile().unwrap(); + assert_eq!("SRTP_AES128_CM_SHA1_80", srtp_profile.name()); + assert_eq!(SrtpProfileId::SRTP_AES128_CM_SHA1_80, srtp_profile.id()); + } + stream + .ssl() + .export_keying_material(&mut buf, "EXTRACTOR-dtls_srtp", None) + .expect("extract"); + + stream.read_exact(&mut [0]).unwrap(); + + let buf2 = guard.join().unwrap(); + + assert_eq!(buf[..], buf2[..]); +} + +/// Tests that when both the client as well as the server use SRTP and their +/// lists of supported protocols have an overlap -- with only ONE protocol +/// being valid for both. +#[test] +fn test_connect_with_srtp_ssl() { + let listener = TcpListener::bind("127.0.0.1:0").unwrap(); + let addr = listener.local_addr().unwrap(); + + let guard = thread::spawn(move || { + let stream = listener.accept().unwrap().0; + let mut ctx = SslContext::builder(SslMethod::dtls()).unwrap(); + ctx.set_certificate_file(Path::new("test/cert.pem"), SslFiletype::PEM) + .unwrap(); + ctx.set_private_key_file(Path::new("test/key.pem"), SslFiletype::PEM) + .unwrap(); + let mut ssl = Ssl::new(&ctx.build()).unwrap(); + ssl.set_tlsext_use_srtp("SRTP_AES128_CM_SHA1_80:SRTP_AES128_CM_SHA1_32") + .unwrap(); + let mut profilenames = String::new(); + for profile in ssl.srtp_profiles().unwrap() { + if !profilenames.is_empty() { + profilenames.push(':'); + } + profilenames += profile.name(); + } + assert_eq!( + "SRTP_AES128_CM_SHA1_80:SRTP_AES128_CM_SHA1_32", + profilenames + ); + ssl.set_mtu(1500).unwrap(); + let mut stream = ssl.accept(stream).unwrap(); + + let mut buf = [0; 60]; + stream + .ssl() + .export_keying_material(&mut buf, "EXTRACTOR-dtls_srtp", None) + .unwrap(); + + stream.write_all(&[0]).unwrap(); + + buf + }); + + let stream = TcpStream::connect(addr).unwrap(); + let ctx = SslContext::builder(SslMethod::dtls()).unwrap(); + let mut ssl = Ssl::new(&ctx.build()).unwrap(); + ssl.set_tlsext_use_srtp("SRTP_AES128_CM_SHA1_80:SRTP_AES128_CM_SHA1_32") + .unwrap(); + ssl.set_mtu(1500).unwrap(); + let mut stream = ssl.connect(stream).unwrap(); + + let mut buf = [1; 60]; + { + let srtp_profile = stream.ssl().selected_srtp_profile().unwrap(); + assert_eq!("SRTP_AES128_CM_SHA1_80", srtp_profile.name()); + assert_eq!(SrtpProfileId::SRTP_AES128_CM_SHA1_80, srtp_profile.id()); + } + stream + .ssl() + .export_keying_material(&mut buf, "EXTRACTOR-dtls_srtp", None) + .expect("extract"); + + stream.read_exact(&mut [0]).unwrap(); + + let buf2 = guard.join().unwrap(); + + assert_eq!(buf[..], buf2[..]); +} + +/// Tests that when the `SslStream` is created as a server stream, the protocols +/// are correctly advertised to the client. +#[test] +fn test_alpn_server_advertise_multiple() { + let mut server = Server::builder(); + server.ctx().set_alpn_select_callback(|_, client| { + ssl::select_next_proto(b"\x08http/1.1\x08spdy/3.1", client).ok_or(ssl::AlpnError::NOACK) + }); + let server = server.build(); + + let mut client = server.client(); + client.ctx().set_alpn_protos(b"\x08spdy/3.1").unwrap(); + let s = client.connect(); + assert_eq!(s.ssl().selected_alpn_protocol(), Some(&b"spdy/3.1"[..])); +} + +#[test] +fn test_alpn_server_select_none_fatal() { + let mut server = Server::builder(); + server.ctx().set_alpn_select_callback(|_, client| { + ssl::select_next_proto(b"\x08http/1.1\x08spdy/3.1", client) + .ok_or(ssl::AlpnError::ALERT_FATAL) + }); + server.should_error(); + let server = server.build(); + + let mut client = server.client(); + client.ctx().set_alpn_protos(b"\x06http/2").unwrap(); + client.connect_err(); +} + +#[test] +fn test_alpn_server_select_none() { + let mut server = Server::builder(); + server.ctx().set_alpn_select_callback(|_, client| { + ssl::select_next_proto(b"\x08http/1.1\x08spdy/3.1", client).ok_or(ssl::AlpnError::NOACK) + }); + let server = server.build(); + + let mut client = server.client(); + client.ctx().set_alpn_protos(b"\x06http/2").unwrap(); + let s = client.connect(); + assert_eq!(None, s.ssl().selected_alpn_protocol()); +} + +#[test] +fn test_alpn_server_unilateral() { + let server = Server::builder().build(); + + let mut client = server.client(); + client.ctx().set_alpn_protos(b"\x06http/2").unwrap(); + let s = client.connect(); + assert_eq!(None, s.ssl().selected_alpn_protocol()); +} + +#[test] +fn test_select_cert_ok() { + let mut server = Server::builder(); + server + .ctx() + .set_select_certificate_callback(|_client_hello| Ok(())); + let server = server.build(); + + let client = server.client(); + client.connect(); +} + +#[test] +fn test_select_cert_error() { + let mut server = Server::builder(); + server.should_error(); + server + .ctx() + .set_select_certificate_callback(|_client_hello| Err(ssl::SelectCertError::ERROR)); + let server = server.build(); + + let client = server.client(); + client.connect_err(); +} + +#[test] +fn test_select_cert_unknown_extension() { + let mut server = Server::builder(); + let unknown_extension = std::sync::Arc::new(std::sync::Mutex::new(Some(vec![]))); + + server.ctx().set_select_certificate_callback({ + let unknown = unknown_extension.clone(); + move |client_hello| { + let ext = client_hello + .get_extension(ExtensionType::SERVER_NAME) + .map(ToOwned::to_owned); + assert!(ext.is_none()); + *unknown.lock().unwrap() = ext; + Ok(()) + } + }); + + let server = server.build(); + let client = server.client(); + + client.connect(); + assert_eq!(unknown_extension.lock().unwrap().as_deref(), None); +} + +#[test] +fn test_select_cert_alpn_extension() { + let mut server = Server::builder(); + let alpn_extension = std::sync::Arc::new(std::sync::Mutex::new(None)); + server.ctx().set_select_certificate_callback({ + let alpn = alpn_extension.clone(); + move |client_hello| { + *alpn.lock().unwrap() = Some( + client_hello + .get_extension(ExtensionType::APPLICATION_LAYER_PROTOCOL_NEGOTIATION) + .unwrap() + .to_owned(), + ); + Ok(()) + } + }); + let server = server.build(); + + let mut client = server.client(); + client.ctx().set_alpn_protos(b"\x06http/2").unwrap(); + client.connect(); + assert_eq!( + alpn_extension.lock().unwrap().as_deref(), + Some(&b"\x00\x07\x06http/2"[..]), + ); +} + +#[test] +#[should_panic(expected = "blammo")] +fn write_panic() { + struct ExplodingStream(TcpStream); + + impl Read for ExplodingStream { + fn read(&mut self, buf: &mut [u8]) -> io::Result { + self.0.read(buf) + } + } + + impl Write for ExplodingStream { + fn write(&mut self, _: &[u8]) -> io::Result { + panic!("blammo"); + } + + fn flush(&mut self) -> io::Result<()> { + self.0.flush() + } + } + + let mut server = Server::builder(); + server.should_error(); + let server = server.build(); + + let stream = ExplodingStream(server.connect_tcp()); + + let ctx = SslContext::builder(SslMethod::tls()).unwrap(); + let _ = Ssl::new(&ctx.build()).unwrap().connect(stream); +} + +#[test] +#[should_panic(expected = "blammo")] +fn read_panic() { + struct ExplodingStream(TcpStream); + + impl Read for ExplodingStream { + fn read(&mut self, _: &mut [u8]) -> io::Result { + panic!("blammo"); + } + } + + impl Write for ExplodingStream { + fn write(&mut self, buf: &[u8]) -> io::Result { + self.0.write(buf) + } + + fn flush(&mut self) -> io::Result<()> { + self.0.flush() + } + } + + let mut server = Server::builder(); + server.should_error(); + let server = server.build(); + + let stream = ExplodingStream(server.connect_tcp()); + + let ctx = SslContext::builder(SslMethod::tls()).unwrap(); + let _ = Ssl::new(&ctx.build()).unwrap().connect(stream); +} + +#[test] +#[should_panic(expected = "blammo")] +fn flush_panic() { + struct ExplodingStream(TcpStream); + + impl Read for ExplodingStream { + fn read(&mut self, buf: &mut [u8]) -> io::Result { + self.0.read(buf) + } + } + + impl Write for ExplodingStream { + fn write(&mut self, buf: &[u8]) -> io::Result { + self.0.write(buf) + } + + fn flush(&mut self) -> io::Result<()> { + panic!("blammo"); + } + } + + let mut server = Server::builder(); + server.should_error(); + let server = server.build(); + + let stream = ExplodingStream(server.connect_tcp()); + + let ctx = SslContext::builder(SslMethod::tls()).unwrap(); + let _ = Ssl::new(&ctx.build()).unwrap().connect(stream); +} + +#[test] +fn refcount_ssl_context() { + let mut ssl = { + let ctx = SslContext::builder(SslMethod::tls()).unwrap(); + ssl::Ssl::new(&ctx.build()).unwrap() + }; + + { + let new_ctx_a = SslContext::builder(SslMethod::tls()).unwrap().build(); + let _new_ctx_b = ssl.set_ssl_context(&new_ctx_a); + } +} + +#[test] +#[cfg_attr(target_os = "windows", ignore)] +#[cfg_attr(all(target_os = "macos", feature = "vendored"), ignore)] +fn default_verify_paths() { + let mut ctx = SslContext::builder(SslMethod::tls()).unwrap(); + ctx.set_default_verify_paths().unwrap(); + ctx.set_verify(SslVerifyMode::PEER); + let ctx = ctx.build(); + let s = match TcpStream::connect("google.com:443") { + Ok(s) => s, + Err(_) => return, + }; + let mut ssl = Ssl::new(&ctx).unwrap(); + ssl.set_hostname("google.com").unwrap(); + let mut socket = ssl.connect(s).unwrap(); + + socket.write_all(b"GET / HTTP/1.0\r\n\r\n").unwrap(); + let mut result = vec![]; + socket.read_to_end(&mut result).unwrap(); + + println!("{}", String::from_utf8_lossy(&result)); + assert!(result.starts_with(b"HTTP/1.0")); + assert!(result.ends_with(b"\r\n") || result.ends_with(b"")); +} + +#[test] +fn add_extra_chain_cert() { + let cert = X509::from_pem(CERT).unwrap(); + let mut ctx = SslContext::builder(SslMethod::tls()).unwrap(); + ctx.add_extra_chain_cert(cert).unwrap(); +} + +#[test] +fn verify_valid_hostname() { + let server = Server::builder().build(); + let mut client = server.client_with_root_ca(); + + client.ctx().set_verify(SslVerifyMode::PEER); + + let mut client = client.build().builder(); + client + .ssl() + .param_mut() + .set_hostflags(X509CheckFlags::NO_PARTIAL_WILDCARDS); + client.ssl().param_mut().set_host("foobar.com").unwrap(); + client.connect(); +} + +#[test] +fn verify_invalid_hostname() { + let mut server = Server::builder(); + + server.should_error(); + + let server = server.build(); + let mut client = server.client_with_root_ca(); + + client.ctx().set_verify(SslVerifyMode::PEER); + + let mut client = client.build().builder(); + client + .ssl() + .param_mut() + .set_hostflags(X509CheckFlags::NO_PARTIAL_WILDCARDS); + client.ssl().param_mut().set_host("bogus.com").unwrap(); + client.connect_err(); +} + +#[test] +fn connector_valid_hostname() { + let server = Server::builder().build(); + + let mut connector = SslConnector::builder(SslMethod::tls()).unwrap(); + connector.set_ca_file("test/root-ca.pem").unwrap(); + + let s = server.connect_tcp(); + let mut s = connector.build().connect("foobar.com", s).unwrap(); + s.read_exact(&mut [0]).unwrap(); +} + +#[test] +fn connector_invalid_hostname() { + let mut server = Server::builder(); + server.should_error(); + let server = server.build(); + + let mut connector = SslConnector::builder(SslMethod::tls()).unwrap(); + connector.set_ca_file("test/root-ca.pem").unwrap(); + + let s = server.connect_tcp(); + connector.build().connect("bogus.com", s).unwrap_err(); +} + +#[test] +fn connector_invalid_no_hostname_verification() { + let server = Server::builder().build(); + + let mut connector = SslConnector::builder(SslMethod::tls()).unwrap(); + connector.set_ca_file("test/root-ca.pem").unwrap(); + + let s = server.connect_tcp(); + let mut s = connector + .build() + .configure() + .unwrap() + .verify_hostname(false) + .connect("bogus.com", s) + .unwrap(); + s.read_exact(&mut [0]).unwrap(); +} + +#[test] +fn connector_no_hostname_still_verifies() { + let mut server = Server::builder(); + server.should_error(); + let server = server.build(); + + let connector = SslConnector::builder(SslMethod::tls()).unwrap().build(); + + let s = server.connect_tcp(); + assert!(connector + .configure() + .unwrap() + .verify_hostname(false) + .connect("fizzbuzz.com", s) + .is_err()); +} + +#[test] +fn connector_no_hostname_can_disable_verify() { + let server = Server::builder().build(); + + let mut connector = SslConnector::builder(SslMethod::tls()).unwrap(); + connector.set_verify(SslVerifyMode::NONE); + let connector = connector.build(); + + let s = server.connect_tcp(); + let mut s = connector + .configure() + .unwrap() + .verify_hostname(false) + .connect("foobar.com", s) + .unwrap(); + s.read_exact(&mut [0]).unwrap(); +} + +fn test_mozilla_server(new: fn(SslMethod) -> Result) { + let listener = TcpListener::bind("127.0.0.1:0").unwrap(); + let port = listener.local_addr().unwrap().port(); + + let t = thread::spawn(move || { + let key = PKey::private_key_from_pem(KEY).unwrap(); + let cert = X509::from_pem(CERT).unwrap(); + let mut acceptor = new(SslMethod::tls()).unwrap(); + acceptor.set_private_key(&key).unwrap(); + acceptor.set_certificate(&cert).unwrap(); + let acceptor = acceptor.build(); + let stream = listener.accept().unwrap().0; + let mut stream = acceptor.accept(stream).unwrap(); + + stream.write_all(b"hello").unwrap(); + }); + + let mut connector = SslConnector::builder(SslMethod::tls()).unwrap(); + connector.set_ca_file("test/root-ca.pem").unwrap(); + let connector = connector.build(); + + let stream = TcpStream::connect(("127.0.0.1", port)).unwrap(); + let mut stream = connector.connect("foobar.com", stream).unwrap(); + + let mut buf = [0; 5]; + stream.read_exact(&mut buf).unwrap(); + assert_eq!(b"hello", &buf); + + t.join().unwrap(); +} + +#[test] +fn connector_client_server_mozilla_intermediate() { + test_mozilla_server(SslAcceptor::mozilla_intermediate); +} + +#[test] +fn connector_client_server_mozilla_modern() { + test_mozilla_server(SslAcceptor::mozilla_modern); +} + +#[test] +fn connector_client_server_mozilla_intermediate_v5() { + test_mozilla_server(SslAcceptor::mozilla_intermediate_v5); +} + +#[test] +fn shutdown() { + let mut server = Server::builder(); + server.io_cb(|mut s| { + assert_eq!(s.read(&mut [0]).unwrap(), 0); + assert_eq!(s.shutdown().unwrap(), ShutdownResult::Received); + }); + let server = server.build(); + + let mut s = server.client().connect(); + + assert_eq!(s.get_shutdown(), ShutdownState::empty()); + assert_eq!(s.shutdown().unwrap(), ShutdownResult::Sent); + assert_eq!(s.get_shutdown(), ShutdownState::SENT); + assert_eq!(s.shutdown().unwrap(), ShutdownResult::Received); + assert_eq!( + s.get_shutdown(), + ShutdownState::SENT | ShutdownState::RECEIVED + ); +} + +#[test] +fn client_ca_list() { + let names = X509Name::load_client_ca_file("test/root-ca.pem").unwrap(); + assert_eq!(names.len(), 1); + + let mut ctx = SslContext::builder(SslMethod::tls()).unwrap(); + ctx.set_client_ca_list(names); +} + +#[test] +fn cert_store() { + let server = Server::builder().build(); + + let mut client = server.client(); + let cert = X509::from_pem(ROOT_CERT).unwrap(); + client.ctx().cert_store_mut().add_cert(cert).unwrap(); + client.ctx().set_verify(SslVerifyMode::PEER); + + client.connect(); +} + +#[test] +fn keying_export() { + let listener = TcpListener::bind("127.0.0.1:0").unwrap(); + let addr = listener.local_addr().unwrap(); + + let label = "EXPERIMENTAL test"; + let context = b"my context"; + + let guard = thread::spawn(move || { + let stream = listener.accept().unwrap().0; + let mut ctx = SslContext::builder(SslMethod::tls()).unwrap(); + ctx.set_certificate_file(Path::new("test/cert.pem"), SslFiletype::PEM) + .unwrap(); + ctx.set_private_key_file(Path::new("test/key.pem"), SslFiletype::PEM) + .unwrap(); + let ssl = Ssl::new(&ctx.build()).unwrap(); + let mut stream = ssl.accept(stream).unwrap(); + + let mut buf = [0; 32]; + stream + .ssl() + .export_keying_material(&mut buf, label, Some(context)) + .unwrap(); + + stream.write_all(&[0]).unwrap(); + + buf + }); + + let stream = TcpStream::connect(addr).unwrap(); + let ctx = SslContext::builder(SslMethod::tls()).unwrap(); + let ssl = Ssl::new(&ctx.build()).unwrap(); + let mut stream = ssl.connect(stream).unwrap(); + + let mut buf = [1; 32]; + stream + .ssl() + .export_keying_material(&mut buf, label, Some(context)) + .unwrap(); + + stream.read_exact(&mut [0]).unwrap(); + + let buf2 = guard.join().unwrap(); + + assert_eq!(buf, buf2); +} + +#[test] +fn no_version_overlap() { + let mut server = Server::builder(); + server.ctx().set_min_proto_version(None).unwrap(); + server + .ctx() + .set_max_proto_version(Some(SslVersion::TLS1_1)) + .unwrap(); + assert_eq!(server.ctx().max_proto_version(), Some(SslVersion::TLS1_1)); + server.should_error(); + let server = server.build(); + + let mut client = server.client(); + client + .ctx() + .set_min_proto_version(Some(SslVersion::TLS1_2)) + .unwrap(); + assert_eq!(client.ctx().min_proto_version(), Some(SslVersion::TLS1_2)); + client.ctx().set_max_proto_version(None).unwrap(); + + client.connect_err(); +} + +fn _check_kinds() { + fn is_send() {} + fn is_sync() {} + + is_send::>(); + is_sync::>(); +} + +#[test] +fn psk_ciphers() { + const CIPHER: &str = "PSK-AES128-CBC-SHA"; + const PSK: &[u8] = b"thisisaverysecurekey"; + const CLIENT_IDENT: &[u8] = b"thisisaclient"; + static CLIENT_CALLED: AtomicBool = AtomicBool::new(false); + static SERVER_CALLED: AtomicBool = AtomicBool::new(false); + + let mut server = Server::builder(); + server.ctx().set_cipher_list(CIPHER).unwrap(); + server.ctx().set_psk_server_callback(|_, identity, psk| { + assert!(identity.unwrap_or(&[]) == CLIENT_IDENT); + psk[..PSK.len()].copy_from_slice(PSK); + SERVER_CALLED.store(true, Ordering::SeqCst); + Ok(PSK.len()) + }); + + let server = server.build(); + + let mut client = server.client(); + // This test relies on TLS 1.2 suites + client.ctx().set_options(super::SslOptions::NO_TLSV1_3); + client.ctx().set_cipher_list(CIPHER).unwrap(); + client + .ctx() + .set_psk_client_callback(move |_, _, identity, psk| { + identity[..CLIENT_IDENT.len()].copy_from_slice(CLIENT_IDENT); + identity[CLIENT_IDENT.len()] = 0; + psk[..PSK.len()].copy_from_slice(PSK); + CLIENT_CALLED.store(true, Ordering::SeqCst); + Ok(PSK.len()) + }); + + client.connect(); + + assert!(CLIENT_CALLED.load(Ordering::SeqCst) && SERVER_CALLED.load(Ordering::SeqCst)); +} + +#[test] +fn sni_callback_swapped_ctx() { + static CALLED_BACK: AtomicBool = AtomicBool::new(false); + + let mut server = Server::builder(); + + let mut ctx = SslContext::builder(SslMethod::tls()).unwrap(); + ctx.set_servername_callback(|_, _| { + CALLED_BACK.store(true, Ordering::SeqCst); + Ok(()) + }); + + let keyed_ctx = mem::replace(server.ctx(), ctx).build(); + server.ssl_cb(move |ssl| ssl.set_ssl_context(&keyed_ctx).unwrap()); + + let server = server.build(); + + server.client().connect(); + + assert!(CALLED_BACK.load(Ordering::SeqCst)); +} + +#[cfg(feature = "kx-safe-default")] +#[test] +fn client_set_default_curves_list() { + let ssl_ctx = crate::ssl::SslContextBuilder::new(SslMethod::tls()) + .unwrap() + .build(); + let mut ssl = Ssl::new(&ssl_ctx).unwrap(); + + // Panics if Kyber768 missing in boringSSL. + ssl.client_set_default_curves_list(); +} + +#[cfg(feature = "kx-safe-default")] +#[test] +fn server_set_default_curves_list() { + let ssl_ctx = crate::ssl::SslContextBuilder::new(SslMethod::tls()) + .unwrap() + .build(); + let mut ssl = Ssl::new(&ssl_ctx).unwrap(); + + // Panics if Kyber768 missing in boringSSL. + ssl.server_set_default_curves_list(); +} diff --git a/crates/boring/src/ssl/test/private_key_method.rs b/crates/boring/src/ssl/test/private_key_method.rs new file mode 100644 index 000000000..019a8c7c4 --- /dev/null +++ b/crates/boring/src/ssl/test/private_key_method.rs @@ -0,0 +1,281 @@ +use once_cell::sync::OnceCell; + +use super::server::{Builder, Server}; +use super::KEY; +use crate::hash::MessageDigest; +use crate::pkey::PKey; +use crate::rsa::Padding; +use crate::sign::{RsaPssSaltlen, Signer}; +use crate::ssl::{ + ErrorCode, HandshakeError, PrivateKeyMethod, PrivateKeyMethodError, SslRef, + SslSignatureAlgorithm, +}; +use std::io::Write; +use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering}; +use std::sync::Arc; + +#[allow(clippy::type_complexity)] +pub(super) struct Method { + sign: Box< + dyn Fn( + &mut SslRef, + &[u8], + SslSignatureAlgorithm, + &mut [u8], + ) -> Result + + Send + + Sync + + 'static, + >, + decrypt: Box< + dyn Fn(&mut SslRef, &[u8], &mut [u8]) -> Result + + Send + + Sync + + 'static, + >, + complete: Box< + dyn Fn(&mut SslRef, &mut [u8]) -> Result + + Send + + Sync + + 'static, + >, +} + +impl Method { + pub(super) fn new() -> Self { + Self { + sign: Box::new(|_, _, _, _| unreachable!("called sign")), + decrypt: Box::new(|_, _, _| unreachable!("called decrypt")), + complete: Box::new(|_, _| unreachable!("called complete")), + } + } + + pub(super) fn sign( + mut self, + sign: impl Fn( + &mut SslRef, + &[u8], + SslSignatureAlgorithm, + &mut [u8], + ) -> Result + + Send + + Sync + + 'static, + ) -> Self { + self.sign = Box::new(sign); + + self + } + + #[allow(dead_code)] + pub(super) fn decrypt( + mut self, + decrypt: impl Fn(&mut SslRef, &[u8], &mut [u8]) -> Result + + Send + + Sync + + 'static, + ) -> Self { + self.decrypt = Box::new(decrypt); + + self + } + + pub(super) fn complete( + mut self, + complete: impl Fn(&mut SslRef, &mut [u8]) -> Result + + Send + + Sync + + 'static, + ) -> Self { + self.complete = Box::new(complete); + + self + } +} + +impl PrivateKeyMethod for Method { + fn sign( + &self, + ssl: &mut SslRef, + input: &[u8], + signature_algorithm: SslSignatureAlgorithm, + output: &mut [u8], + ) -> Result { + (self.sign)(ssl, input, signature_algorithm, output) + } + + fn decrypt( + &self, + ssl: &mut SslRef, + input: &[u8], + output: &mut [u8], + ) -> Result { + (self.decrypt)(ssl, input, output) + } + + fn complete( + &self, + ssl: &mut SslRef, + output: &mut [u8], + ) -> Result { + (self.complete)(ssl, output) + } +} + +fn builder_with_private_key_method(method: Method) -> Builder { + let mut builder = Server::builder(); + + builder.ctx().set_private_key_method(method); + + builder +} + +#[test] +fn test_sign_failure() { + let called_sign = Arc::new(AtomicBool::new(false)); + let called_sign_clone = called_sign.clone(); + + let mut builder = builder_with_private_key_method(Method::new().sign(move |_, _, _, _| { + called_sign_clone.store(true, Ordering::SeqCst); + + Err(PrivateKeyMethodError::FAILURE) + })); + + builder.err_cb(|error| { + let HandshakeError::Failure(mid_handshake) = error else { + panic!("should be Failure"); + }; + + assert_eq!(mid_handshake.error().code(), ErrorCode::SSL); + }); + + let server = builder.build(); + let client = server.client_with_root_ca(); + + client.connect_err(); + + assert!(called_sign.load(Ordering::SeqCst)); +} + +#[test] +fn test_sign_retry_complete_failure() { + let called_complete = Arc::new(AtomicUsize::new(0)); + let called_complete_clone = called_complete.clone(); + + let mut builder = builder_with_private_key_method( + Method::new() + .sign(|_, _, _, _| Err(PrivateKeyMethodError::RETRY)) + .complete(move |_, _| { + let old = called_complete_clone.fetch_add(1, Ordering::SeqCst); + + Err(if old == 0 { + PrivateKeyMethodError::RETRY + } else { + PrivateKeyMethodError::FAILURE + }) + }), + ); + + builder.err_cb(|error| { + let HandshakeError::WouldBlock(mid_handshake) = error else { + panic!("should be WouldBlock"); + }; + + assert!(mid_handshake.error().would_block()); + assert_eq!( + mid_handshake.error().code(), + ErrorCode::WANT_PRIVATE_KEY_OPERATION + ); + + let HandshakeError::WouldBlock(mid_handshake) = mid_handshake.handshake().unwrap_err() + else { + panic!("should be WouldBlock"); + }; + + assert_eq!( + mid_handshake.error().code(), + ErrorCode::WANT_PRIVATE_KEY_OPERATION + ); + + let HandshakeError::Failure(mid_handshake) = mid_handshake.handshake().unwrap_err() else { + panic!("should be Failure"); + }; + + assert_eq!(mid_handshake.error().code(), ErrorCode::SSL); + }); + + let server = builder.build(); + let client = server.client_with_root_ca(); + + client.connect_err(); + + assert_eq!(called_complete.load(Ordering::SeqCst), 2); +} + +#[test] +fn test_sign_ok() { + let server = builder_with_private_key_method(Method::new().sign( + |_, input, signature_algorithm, output| { + assert_eq!( + signature_algorithm, + SslSignatureAlgorithm::RSA_PSS_RSAE_SHA256, + ); + + Ok(sign_with_default_config(input, output)) + }, + )) + .build(); + + let client = server.client_with_root_ca(); + + client.connect(); +} + +#[test] +fn test_sign_retry_complete_ok() { + let input_cell = Arc::new(OnceCell::new()); + let input_cell_clone = input_cell.clone(); + + let mut builder = builder_with_private_key_method( + Method::new() + .sign(move |_, input, _, _| { + input_cell.set(input.to_owned()).unwrap(); + + Err(PrivateKeyMethodError::RETRY) + }) + .complete(move |_, output| { + let input = input_cell_clone.get().unwrap(); + + Ok(sign_with_default_config(input, output)) + }), + ); + + builder.err_cb(|error| { + let HandshakeError::WouldBlock(mid_handshake) = error else { + panic!("should be WouldBlock"); + }; + + let mut socket = mid_handshake.handshake().unwrap(); + + socket.write_all(&[0]).unwrap(); + }); + + let server = builder.build(); + let client = server.client_with_root_ca(); + + client.connect(); +} + +fn sign_with_default_config(input: &[u8], output: &mut [u8]) -> usize { + let pkey = PKey::private_key_from_pem(KEY).unwrap(); + let mut signer = Signer::new(MessageDigest::sha256(), &pkey).unwrap(); + + signer.set_rsa_padding(Padding::PKCS1_PSS).unwrap(); + signer + .set_rsa_pss_saltlen(RsaPssSaltlen::DIGEST_LENGTH) + .unwrap(); + + signer.update(input).unwrap(); + + signer.sign(output).unwrap() +} diff --git a/crates/boring/src/ssl/test/server.rs b/crates/boring/src/ssl/test/server.rs new file mode 100644 index 000000000..e5c0497c0 --- /dev/null +++ b/crates/boring/src/ssl/test/server.rs @@ -0,0 +1,202 @@ +use std::io::{Read, Write}; +use std::net::{SocketAddr, TcpListener, TcpStream}; +use std::thread::{self, JoinHandle}; + +use crate::ssl::{ + HandshakeError, Ssl, SslContext, SslContextBuilder, SslFiletype, SslMethod, SslRef, SslStream, +}; + +pub struct Server { + handle: Option>, + addr: SocketAddr, +} + +impl Drop for Server { + fn drop(&mut self) { + if !thread::panicking() { + self.handle.take().unwrap().join().unwrap(); + } + } +} + +impl Server { + pub fn builder() -> Builder { + let mut ctx = SslContext::builder(SslMethod::tls()).unwrap(); + ctx.set_certificate_chain_file("test/cert.pem").unwrap(); + ctx.set_private_key_file("test/key.pem", SslFiletype::PEM) + .unwrap(); + + Builder { + ctx, + ssl_cb: Box::new(|_| {}), + io_cb: Box::new(|_| {}), + err_cb: Box::new(|_| {}), + should_error: false, + expected_connections_count: 1, + } + } + + pub fn client(&self) -> ClientBuilder { + ClientBuilder { + ctx: SslContext::builder(SslMethod::tls()).unwrap(), + addr: self.addr, + } + } + + pub fn client_with_root_ca(&self) -> ClientBuilder { + let mut client = self.client(); + + client.ctx().set_ca_file("test/root-ca.pem").unwrap(); + + client + } + + pub fn connect_tcp(&self) -> TcpStream { + TcpStream::connect(self.addr).unwrap() + } +} + +pub struct Builder { + ctx: SslContextBuilder, + ssl_cb: Box, + io_cb: Box) + Send>, + err_cb: Box) + Send>, + should_error: bool, + expected_connections_count: usize, +} + +impl Builder { + pub fn ctx(&mut self) -> &mut SslContextBuilder { + &mut self.ctx + } + + pub fn ssl_cb(&mut self, cb: F) + where + F: 'static + FnMut(&mut SslRef) + Send, + { + self.ssl_cb = Box::new(cb); + } + + pub fn io_cb(&mut self, cb: F) + where + F: 'static + FnMut(SslStream) + Send, + { + self.io_cb = Box::new(cb); + } + + pub fn err_cb(&mut self, cb: impl FnMut(HandshakeError) + Send + 'static) { + self.should_error(); + + self.err_cb = Box::new(cb); + } + + pub fn should_error(&mut self) { + self.should_error = true; + } + + pub fn expected_connections_count(&mut self, count: usize) { + self.expected_connections_count = count; + } + + pub fn build(self) -> Server { + let ctx = self.ctx.build(); + let socket = TcpListener::bind("127.0.0.1:0").unwrap(); + let addr = socket.local_addr().unwrap(); + let mut ssl_cb = self.ssl_cb; + let mut io_cb = self.io_cb; + let mut err_cb = self.err_cb; + let should_error = self.should_error; + let mut count = self.expected_connections_count; + + let handle = thread::spawn(move || { + while count > 0 { + let socket = socket.accept().unwrap().0; + let mut ssl = Ssl::new(&ctx).unwrap(); + + ssl_cb(&mut ssl); + + let r = ssl.accept(socket); + + if should_error { + err_cb(r.unwrap_err()); + } else { + let mut socket = r.unwrap(); + + socket.write_all(&[0]).unwrap(); + io_cb(socket); + } + + count -= 1; + } + }); + + Server { + handle: Some(handle), + addr, + } + } +} + +pub struct ClientBuilder { + ctx: SslContextBuilder, + addr: SocketAddr, +} + +impl ClientBuilder { + pub fn ctx(&mut self) -> &mut SslContextBuilder { + &mut self.ctx + } + + pub fn build(self) -> Client { + Client { + ctx: self.ctx.build(), + addr: self.addr, + } + } + + pub fn connect(self) -> SslStream { + self.build().builder().connect() + } + + pub fn connect_err(self) -> HandshakeError { + self.build().builder().connect_err() + } +} + +pub struct Client { + ctx: SslContext, + addr: SocketAddr, +} + +impl Client { + pub fn builder(&self) -> ClientSslBuilder { + ClientSslBuilder { + ssl: Ssl::new(&self.ctx).unwrap(), + addr: self.addr, + } + } +} + +pub struct ClientSslBuilder { + ssl: Ssl, + addr: SocketAddr, +} + +impl ClientSslBuilder { + pub fn ssl(&mut self) -> &mut SslRef { + &mut self.ssl + } + + pub fn connect(self) -> SslStream { + let socket = TcpStream::connect(self.addr).unwrap(); + let mut s = self.ssl.connect(socket).unwrap(); + s.read_exact(&mut [0]).unwrap(); + s + } + + pub fn connect_err(self) -> HandshakeError { + let socket = TcpStream::connect(self.addr).unwrap(); + + self.ssl.setup_connect(socket).handshake().unwrap_err() + } +} diff --git a/crates/boring/src/ssl/test/session.rs b/crates/boring/src/ssl/test/session.rs new file mode 100644 index 000000000..23c0f4d5d --- /dev/null +++ b/crates/boring/src/ssl/test/session.rs @@ -0,0 +1,186 @@ +use std::io::Write; +use std::sync::atomic::{AtomicBool, Ordering}; +use std::sync::OnceLock; + +use crate::ssl::test::server::Server; +use crate::ssl::{ + ErrorCode, GetSessionPendingError, HandshakeError, Ssl, SslContext, SslContextBuilder, + SslMethod, SslOptions, SslSession, SslSessionCacheMode, SslVersion, +}; + +#[test] +fn idle_session() { + let ctx = SslContext::builder(SslMethod::tls()).unwrap().build(); + let ssl = Ssl::new(&ctx).unwrap(); + assert!(ssl.session().is_none()); +} + +#[test] +fn active_session() { + let server = Server::builder().build(); + + let s = server.client().connect(); + + let session = s.ssl().session().unwrap(); + let len = session.master_key_len(); + let mut buf = vec![0; len - 1]; + let copied = session.master_key(&mut buf); + assert_eq!(copied, buf.len()); + let mut buf = vec![0; len + 1]; + let copied = session.master_key(&mut buf); + assert_eq!(copied, len); +} + +#[test] +fn new_get_session_callback() { + static FOUND_SESSION: AtomicBool = AtomicBool::new(false); + static SERVER_SESSION_DER: OnceLock> = OnceLock::new(); + static CLIENT_SESSION_DER: OnceLock> = OnceLock::new(); + + let mut server = Server::builder(); + + server.expected_connections_count(2); + server + .ctx() + .set_max_proto_version(Some(SslVersion::TLS1_2)) + .unwrap(); + server.ctx().set_options(SslOptions::NO_TICKET); + server + .ctx() + .set_session_cache_mode(SslSessionCacheMode::SERVER | SslSessionCacheMode::NO_INTERNAL); + server.ctx().set_new_session_callback(|_, session| { + SERVER_SESSION_DER.set(session.to_der().unwrap()).unwrap() + }); + unsafe { + server.ctx().set_get_session_callback(|_, id| { + let Some(der) = SERVER_SESSION_DER.get() else { + return Ok(None); + }; + + let session = SslSession::from_der(der).unwrap(); + + FOUND_SESSION.store(true, Ordering::SeqCst); + + assert_eq!(id, session.id()); + + Ok(Some(session)) + }); + } + server.ctx().set_session_id_context(b"foo").unwrap(); + + let server = server.build(); + + let mut client = server.client(); + + client + .ctx() + .set_session_cache_mode(SslSessionCacheMode::CLIENT); + client.ctx().set_new_session_callback(|_, session| { + CLIENT_SESSION_DER.set(session.to_der().unwrap()).unwrap() + }); + + let client = client.build(); + + client.builder().connect(); + + assert!(CLIENT_SESSION_DER.get().is_some()); + assert!(SERVER_SESSION_DER.get().is_some()); + assert!(!FOUND_SESSION.load(Ordering::SeqCst)); + + let mut ssl_builder = client.builder(); + + unsafe { + ssl_builder + .ssl() + .set_session(&SslSession::from_der(CLIENT_SESSION_DER.get().unwrap()).unwrap()) + .unwrap(); + } + + ssl_builder.connect(); + + assert!(FOUND_SESSION.load(Ordering::SeqCst)); +} + +#[test] +fn new_get_session_callback_pending() { + static CALLED_SERVER_CALLBACK: AtomicBool = AtomicBool::new(false); + + let mut server = Server::builder(); + + server + .ctx() + .set_max_proto_version(Some(SslVersion::TLS1_2)) + .unwrap(); + server.ctx().set_options(SslOptions::NO_TICKET); + server + .ctx() + .set_session_cache_mode(SslSessionCacheMode::SERVER | SslSessionCacheMode::NO_INTERNAL); + unsafe { + server.ctx().set_get_session_callback(|_, _| { + if !CALLED_SERVER_CALLBACK.swap(true, Ordering::SeqCst) { + return Err(GetSessionPendingError); + } + + Ok(None) + }); + } + server.ctx().set_session_id_context(b"foo").unwrap(); + server.err_cb(|error| { + let HandshakeError::WouldBlock(mid_handshake) = error else { + panic!("should be WouldBlock"); + }; + + assert!(mid_handshake.error().would_block()); + assert_eq!(mid_handshake.error().code(), ErrorCode::PENDING_SESSION); + + let mut socket = mid_handshake.handshake().unwrap(); + + socket.write_all(&[0]).unwrap(); + }); + + let server = server.build(); + + let mut client = server.client(); + + client + .ctx() + .set_session_cache_mode(SslSessionCacheMode::CLIENT); + + client.connect(); +} + +#[test] +fn new_session_callback_swapped_ctx() { + static CALLED_BACK: AtomicBool = AtomicBool::new(false); + + let mut server = Server::builder(); + server.ctx().set_session_id_context(b"foo").unwrap(); + + let server = server.build(); + + let mut client = server.client(); + + client + .ctx() + .set_session_cache_mode(SslSessionCacheMode::CLIENT | SslSessionCacheMode::NO_INTERNAL); + client + .ctx() + .set_new_session_callback(|_, _| CALLED_BACK.store(true, Ordering::SeqCst)); + + let mut client = client.build().builder(); + + let ctx = SslContextBuilder::new(SslMethod::tls()).unwrap().build(); + client.ssl().set_ssl_context(&ctx).unwrap(); + + client.connect(); + + assert!(CALLED_BACK.load(Ordering::SeqCst)); +} + +#[test] +fn session_cache_size() { + let mut ctx = SslContext::builder(SslMethod::tls()).unwrap(); + ctx.set_session_cache_size(1234); + let ctx = ctx.build(); + assert_eq!(ctx.session_cache_size(), 1234); +} diff --git a/crates/boring/src/stack.rs b/crates/boring/src/stack.rs new file mode 100644 index 000000000..67b952739 --- /dev/null +++ b/crates/boring/src/stack.rs @@ -0,0 +1,375 @@ +use crate::ffi; +use foreign_types::{ForeignType, ForeignTypeRef, Opaque}; +use libc::size_t; +use std::borrow::Borrow; +use std::convert::AsRef; +use std::fmt; +use std::iter; +use std::marker::PhantomData; +use std::mem; +use std::ops::{Deref, DerefMut, Index, IndexMut, Range}; + +use crate::error::ErrorStack; +use crate::{cvt_0, cvt_p}; + +use crate::ffi::{ + sk_free as OPENSSL_sk_free, sk_new_null as OPENSSL_sk_new_null, sk_num as OPENSSL_sk_num, + sk_pop as OPENSSL_sk_pop, sk_push as OPENSSL_sk_push, sk_value as OPENSSL_sk_value, + _STACK as OPENSSL_STACK, +}; + +/// Trait implemented by types which can be placed in a stack. +/// +/// It should not be implemented for any type outside of this crate. +pub trait Stackable: ForeignType { + /// The C stack type for this element. + /// + /// Generally called `stack_st_{ELEMENT_TYPE}`, normally hidden by the + /// `STACK_OF(ELEMENT_TYPE)` macro in the OpenSSL API. + type StackType; +} + +/// An owned stack of `T`. +pub struct Stack(*mut T::StackType); + +unsafe impl Send for Stack {} +unsafe impl Sync for Stack {} + +impl fmt::Debug for Stack +where + T: Stackable, + T::Ref: fmt::Debug, +{ + fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { + fmt.debug_list().entries(self).finish() + } +} +impl Drop for Stack { + fn drop(&mut self) { + unsafe { + while self.pop().is_some() {} + OPENSSL_sk_free(self.0 as *mut _); + } + } +} + +impl Stack { + pub fn new() -> Result, ErrorStack> { + unsafe { + ffi::init(); + let ptr = cvt_p(OPENSSL_sk_new_null())?; + Ok(Stack(ptr as *mut _)) + } + } +} + +impl iter::IntoIterator for Stack { + type IntoIter = IntoIter; + type Item = T; + + fn into_iter(self) -> IntoIter { + let it = IntoIter { + stack: self.0, + idxs: 0..self.len(), + }; + mem::forget(self); + it + } +} + +impl AsRef> for Stack { + fn as_ref(&self) -> &StackRef { + self + } +} + +impl Borrow> for Stack { + fn borrow(&self) -> &StackRef { + self + } +} + +unsafe impl ForeignType for Stack { + type CType = T::StackType; + type Ref = StackRef; + + #[inline] + unsafe fn from_ptr(ptr: *mut T::StackType) -> Stack { + assert!( + !ptr.is_null(), + "Must not instantiate a Stack from a null-ptr - use Stack::new() in \ + that case" + ); + Stack(ptr) + } + + #[inline] + fn as_ptr(&self) -> *mut T::StackType { + self.0 + } +} + +impl Deref for Stack { + type Target = StackRef; + + fn deref(&self) -> &StackRef { + unsafe { StackRef::from_ptr(self.0) } + } +} + +impl DerefMut for Stack { + fn deref_mut(&mut self) -> &mut StackRef { + unsafe { StackRef::from_ptr_mut(self.0) } + } +} + +pub struct IntoIter { + stack: *mut T::StackType, + idxs: Range, +} + +impl Drop for IntoIter { + fn drop(&mut self) { + unsafe { + for _ in &mut *self {} + OPENSSL_sk_free(self.stack as *mut _); + } + } +} + +impl Iterator for IntoIter { + type Item = T; + + fn next(&mut self) -> Option { + unsafe { + self.idxs + .next() + .map(|i| T::from_ptr(OPENSSL_sk_value(self.stack as *mut _, i) as *mut _)) + } + } + + fn size_hint(&self) -> (usize, Option) { + self.idxs.size_hint() + } +} + +impl DoubleEndedIterator for IntoIter { + fn next_back(&mut self) -> Option { + unsafe { + self.idxs + .next_back() + .map(|i| T::from_ptr(OPENSSL_sk_value(self.stack as *mut _, i) as *mut _)) + } + } +} + +impl ExactSizeIterator for IntoIter {} + +pub struct StackRef(Opaque, PhantomData); + +unsafe impl Send for StackRef {} +unsafe impl Sync for StackRef {} + +unsafe impl ForeignTypeRef for StackRef { + type CType = T::StackType; +} + +impl StackRef { + fn as_stack(&self) -> *mut OPENSSL_STACK { + self.as_ptr() as *mut _ + } + + /// Returns the number of items in the stack. + pub fn len(&self) -> usize { + unsafe { OPENSSL_sk_num(self.as_stack()) } + } + + /// Determines if the stack is empty. + pub fn is_empty(&self) -> bool { + self.len() == 0 + } + + pub fn iter(&self) -> Iter { + Iter { + stack: self, + idxs: 0..self.len(), + } + } + + pub fn iter_mut(&mut self) -> IterMut { + IterMut { + idxs: 0..self.len(), + stack: self, + } + } + + /// Returns a reference to the element at the given index in the + /// stack or `None` if the index is out of bounds + pub fn get(&self, idx: usize) -> Option<&T::Ref> { + unsafe { + if idx >= self.len() { + return None; + } + + Some(T::Ref::from_ptr(self._get(idx))) + } + } + + /// Returns a mutable reference to the element at the given index in the + /// stack or `None` if the index is out of bounds + pub fn get_mut(&mut self, idx: usize) -> Option<&mut T::Ref> { + unsafe { + if idx >= self.len() { + return None; + } + + Some(T::Ref::from_ptr_mut(self._get(idx))) + } + } + + /// Pushes a value onto the top of the stack. + pub fn push(&mut self, data: T) -> Result<(), ErrorStack> { + unsafe { + cvt_0(OPENSSL_sk_push(self.as_stack(), data.as_ptr() as *mut _))?; + mem::forget(data); + Ok(()) + } + } + + /// Removes the last element from the stack and returns it. + pub fn pop(&mut self) -> Option { + unsafe { + let ptr = OPENSSL_sk_pop(self.as_stack()); + if ptr.is_null() { + None + } else { + Some(T::from_ptr(ptr as *mut _)) + } + } + } + + unsafe fn _get(&self, idx: usize) -> *mut T::CType { + OPENSSL_sk_value(self.as_stack(), idx) as *mut _ + } +} + +impl Index for StackRef { + type Output = T::Ref; + + fn index(&self, index: usize) -> &T::Ref { + self.get(index).unwrap() + } +} + +impl IndexMut for StackRef { + fn index_mut(&mut self, index: usize) -> &mut T::Ref { + self.get_mut(index).unwrap() + } +} + +impl<'a, T: Stackable> iter::IntoIterator for &'a StackRef { + type Item = &'a T::Ref; + type IntoIter = Iter<'a, T>; + + fn into_iter(self) -> Iter<'a, T> { + self.iter() + } +} + +impl<'a, T: Stackable> iter::IntoIterator for &'a mut StackRef { + type Item = &'a mut T::Ref; + type IntoIter = IterMut<'a, T>; + + fn into_iter(self) -> IterMut<'a, T> { + self.iter_mut() + } +} + +impl<'a, T: Stackable> iter::IntoIterator for &'a Stack { + type Item = &'a T::Ref; + type IntoIter = Iter<'a, T>; + + fn into_iter(self) -> Iter<'a, T> { + self.iter() + } +} + +impl<'a, T: Stackable> iter::IntoIterator for &'a mut Stack { + type Item = &'a mut T::Ref; + type IntoIter = IterMut<'a, T>; + + fn into_iter(self) -> IterMut<'a, T> { + self.iter_mut() + } +} + +/// An iterator over the stack's contents. +pub struct Iter<'a, T: Stackable> +where + T: 'a, +{ + stack: &'a StackRef, + idxs: Range, +} + +impl<'a, T: Stackable> Iterator for Iter<'a, T> { + type Item = &'a T::Ref; + + fn next(&mut self) -> Option<&'a T::Ref> { + unsafe { + self.idxs + .next() + .map(|i| T::Ref::from_ptr(OPENSSL_sk_value(self.stack.as_stack(), i) as *mut _)) + } + } + + fn size_hint(&self) -> (usize, Option) { + self.idxs.size_hint() + } +} + +impl<'a, T: Stackable> DoubleEndedIterator for Iter<'a, T> { + fn next_back(&mut self) -> Option<&'a T::Ref> { + unsafe { + self.idxs + .next_back() + .map(|i| T::Ref::from_ptr(OPENSSL_sk_value(self.stack.as_stack(), i) as *mut _)) + } + } +} + +impl<'a, T: Stackable> ExactSizeIterator for Iter<'a, T> {} + +/// A mutable iterator over the stack's contents. +pub struct IterMut<'a, T: Stackable + 'a> { + stack: &'a mut StackRef, + idxs: Range, +} + +impl<'a, T: Stackable> Iterator for IterMut<'a, T> { + type Item = &'a mut T::Ref; + + fn next(&mut self) -> Option<&'a mut T::Ref> { + unsafe { + self.idxs + .next() + .map(|i| T::Ref::from_ptr_mut(OPENSSL_sk_value(self.stack.as_stack(), i) as *mut _)) + } + } + + fn size_hint(&self) -> (usize, Option) { + self.idxs.size_hint() + } +} + +impl<'a, T: Stackable> DoubleEndedIterator for IterMut<'a, T> { + fn next_back(&mut self) -> Option<&'a mut T::Ref> { + unsafe { + self.idxs + .next_back() + .map(|i| T::Ref::from_ptr_mut(OPENSSL_sk_value(self.stack.as_stack(), i) as *mut _)) + } + } +} + +impl<'a, T: Stackable> ExactSizeIterator for IterMut<'a, T> {} diff --git a/crates/boring/src/string.rs b/crates/boring/src/string.rs new file mode 100644 index 000000000..05c401f16 --- /dev/null +++ b/crates/boring/src/string.rs @@ -0,0 +1,84 @@ +use crate::ffi; +use foreign_types::ForeignTypeRef; +use libc::{c_char, c_void}; +use std::convert::AsRef; +use std::ffi::CStr; +use std::fmt; +use std::ops::Deref; +use std::str; + +use crate::stack::Stackable; + +foreign_type_and_impl_send_sync! { + type CType = c_char; + fn drop = free; + + pub struct OpensslString; +} + +impl fmt::Display for OpensslString { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fmt::Display::fmt(&**self, f) + } +} + +impl fmt::Debug for OpensslString { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fmt::Debug::fmt(&**self, f) + } +} + +impl Stackable for OpensslString { + type StackType = ffi::stack_st_OPENSSL_STRING; +} + +impl AsRef for OpensslString { + fn as_ref(&self) -> &str { + self + } +} + +impl AsRef<[u8]> for OpensslString { + fn as_ref(&self) -> &[u8] { + self.as_bytes() + } +} + +impl Deref for OpensslStringRef { + type Target = str; + + fn deref(&self) -> &str { + unsafe { + let slice = CStr::from_ptr(self.as_ptr()).to_bytes(); + str::from_utf8_unchecked(slice) + } + } +} + +impl AsRef for OpensslStringRef { + fn as_ref(&self) -> &str { + self + } +} + +impl AsRef<[u8]> for OpensslStringRef { + fn as_ref(&self) -> &[u8] { + self.as_bytes() + } +} + +impl fmt::Display for OpensslStringRef { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fmt::Display::fmt(&**self, f) + } +} + +impl fmt::Debug for OpensslStringRef { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fmt::Debug::fmt(&**self, f) + } +} + +unsafe fn free(buf: *mut c_char) { + crate::ffi::OPENSSL_free(buf as *mut c_void); +} diff --git a/crates/boring/src/symm.rs b/crates/boring/src/symm.rs new file mode 100644 index 000000000..55a60999d --- /dev/null +++ b/crates/boring/src/symm.rs @@ -0,0 +1,964 @@ +//! High level interface to certain symmetric ciphers. +//! +//! # Examples +//! +//! Encrypt data in AES128 CBC mode +//! +//! ``` +//! use boring::symm::{encrypt, Cipher}; +//! +//! let cipher = Cipher::aes_128_cbc(); +//! let data = b"Some Crypto Text"; +//! let key = b"\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F"; +//! let iv = b"\x00\x01\x02\x03\x04\x05\x06\x07\x00\x01\x02\x03\x04\x05\x06\x07"; +//! let ciphertext = encrypt( +//! cipher, +//! key, +//! Some(iv), +//! data).unwrap(); +//! +//! assert_eq!( +//! b"\xB4\xB9\xE7\x30\xD6\xD6\xF7\xDE\x77\x3F\x1C\xFF\xB3\x3E\x44\x5A\x91\xD7\x27\x62\x87\x4D\ +//! \xFB\x3C\x5E\xC4\x59\x72\x4A\xF4\x7C\xA1", +//! &ciphertext[..]); +//! ``` +//! +//! Encrypting an asymmetric key with a symmetric cipher +//! +//! ``` +//! use boring::rsa::{Padding, Rsa}; +//! use boring::symm::Cipher; +//! +//! // Generate keypair and encrypt private key: +//! let keypair = Rsa::generate(2048).unwrap(); +//! let cipher = Cipher::aes_256_cbc(); +//! let pubkey_pem = keypair.public_key_to_pem_pkcs1().unwrap(); +//! let privkey_pem = keypair.private_key_to_pem_passphrase(cipher, b"Rust").unwrap(); +//! // pubkey_pem and privkey_pem could be written to file here. +//! +//! // Load private and public key from string: +//! let pubkey = Rsa::public_key_from_pem_pkcs1(&pubkey_pem).unwrap(); +//! let privkey = Rsa::private_key_from_pem_passphrase(&privkey_pem, b"Rust").unwrap(); +//! +//! // Use the asymmetric keys to encrypt and decrypt a short message: +//! let msg = b"Foo bar"; +//! let mut encrypted = vec![0; pubkey.size() as usize]; +//! let mut decrypted = vec![0; privkey.size() as usize]; +//! let len = pubkey.public_encrypt(msg, &mut encrypted, Padding::PKCS1).unwrap(); +//! assert!(len > msg.len()); +//! let len = privkey.private_decrypt(&encrypted, &mut decrypted, Padding::PKCS1).unwrap(); +//! let output_string = String::from_utf8(decrypted[..len].to_vec()).unwrap(); +//! assert_eq!("Foo bar", output_string); +//! println!("Decrypted: '{}'", output_string); +//! ``` + +use crate::ffi; +use libc::{c_int, c_uint}; +use std::cmp; +use std::ptr; + +use crate::error::ErrorStack; +use crate::nid::Nid; +use crate::{cvt, cvt_p}; + +#[derive(Copy, Clone)] +pub enum Mode { + Encrypt, + Decrypt, +} + +/// Represents a particular cipher algorithm. +/// +/// See OpenSSL doc at [`EVP_EncryptInit`] for more information on each algorithms. +/// +/// [`EVP_EncryptInit`]: https://www.openssl.org/docs/man1.1.0/crypto/EVP_EncryptInit.html +#[derive(Copy, Clone, PartialEq, Eq)] +pub struct Cipher(*const ffi::EVP_CIPHER); + +impl Cipher { + /// Looks up the cipher for a certain nid. + /// + /// This corresponds to [`EVP_get_cipherbynid`] + /// + /// [`EVP_get_cipherbynid`]: https://www.openssl.org/docs/man1.0.2/crypto/EVP_get_cipherbyname.html + pub fn from_nid(nid: Nid) -> Option { + let ptr = unsafe { ffi::EVP_get_cipherbyname(ffi::OBJ_nid2sn(nid.as_raw())) }; + if ptr.is_null() { + None + } else { + Some(Cipher(ptr)) + } + } + + pub fn aes_128_ecb() -> Cipher { + unsafe { Cipher(ffi::EVP_aes_128_ecb()) } + } + + pub fn aes_128_cbc() -> Cipher { + unsafe { Cipher(ffi::EVP_aes_128_cbc()) } + } + + pub fn aes_128_ctr() -> Cipher { + unsafe { Cipher(ffi::EVP_aes_128_ctr()) } + } + + pub fn aes_128_gcm() -> Cipher { + unsafe { Cipher(ffi::EVP_aes_128_gcm()) } + } + + pub fn aes_128_ofb() -> Cipher { + unsafe { Cipher(ffi::EVP_aes_128_ofb()) } + } + + pub fn aes_192_ecb() -> Cipher { + unsafe { Cipher(ffi::EVP_aes_192_ecb()) } + } + + pub fn aes_192_cbc() -> Cipher { + unsafe { Cipher(ffi::EVP_aes_192_cbc()) } + } + + pub fn aes_192_ctr() -> Cipher { + unsafe { Cipher(ffi::EVP_aes_192_ctr()) } + } + + pub fn aes_192_gcm() -> Cipher { + unsafe { Cipher(ffi::EVP_aes_192_gcm()) } + } + + pub fn aes_192_ofb() -> Cipher { + unsafe { Cipher(ffi::EVP_aes_192_ofb()) } + } + + pub fn aes_256_ecb() -> Cipher { + unsafe { Cipher(ffi::EVP_aes_256_ecb()) } + } + + pub fn aes_256_cbc() -> Cipher { + unsafe { Cipher(ffi::EVP_aes_256_cbc()) } + } + + pub fn aes_256_ctr() -> Cipher { + unsafe { Cipher(ffi::EVP_aes_256_ctr()) } + } + + pub fn aes_256_gcm() -> Cipher { + unsafe { Cipher(ffi::EVP_aes_256_gcm()) } + } + + pub fn aes_256_ofb() -> Cipher { + unsafe { Cipher(ffi::EVP_aes_256_ofb()) } + } + + pub fn des_cbc() -> Cipher { + unsafe { Cipher(ffi::EVP_des_cbc()) } + } + + pub fn des_ecb() -> Cipher { + unsafe { Cipher(ffi::EVP_des_ecb()) } + } + + pub fn des_ede3() -> Cipher { + unsafe { Cipher(ffi::EVP_des_ede3()) } + } + + pub fn des_ede3_cbc() -> Cipher { + unsafe { Cipher(ffi::EVP_des_ede3_cbc()) } + } + + pub fn rc4() -> Cipher { + unsafe { Cipher(ffi::EVP_rc4()) } + } + + /// Creates a `Cipher` from a raw pointer to its OpenSSL type. + /// + /// # Safety + /// + /// The caller must ensure the pointer is valid for the `'static` lifetime. + pub unsafe fn from_ptr(ptr: *const ffi::EVP_CIPHER) -> Cipher { + Cipher(ptr) + } + + #[allow(clippy::trivially_copy_pass_by_ref)] + pub fn as_ptr(&self) -> *const ffi::EVP_CIPHER { + self.0 + } + + /// Returns the length of keys used with this cipher. + #[allow(clippy::trivially_copy_pass_by_ref)] + pub fn key_len(&self) -> usize { + unsafe { EVP_CIPHER_key_length(self.0) as usize } + } + + /// Returns the length of the IV used with this cipher, or `None` if the + /// cipher does not use an IV. + #[allow(clippy::trivially_copy_pass_by_ref)] + pub fn iv_len(&self) -> Option { + unsafe { + let len = EVP_CIPHER_iv_length(self.0) as usize; + if len == 0 { + None + } else { + Some(len) + } + } + } + + /// Returns the block size of the cipher. + /// + /// # Note + /// + /// Stream ciphers such as RC4 have a block size of 1. + #[allow(clippy::trivially_copy_pass_by_ref)] + pub fn block_size(&self) -> usize { + unsafe { EVP_CIPHER_block_size(self.0) as usize } + } +} + +unsafe impl Sync for Cipher {} +unsafe impl Send for Cipher {} + +/// Represents a symmetric cipher context. +/// +/// Padding is enabled by default. +/// +/// # Examples +/// +/// Encrypt some plaintext in chunks, then decrypt the ciphertext back into plaintext, in AES 128 +/// CBC mode. +/// +/// ``` +/// use boring::symm::{Cipher, Mode, Crypter}; +/// +/// let plaintexts: [&[u8]; 2] = [b"Some Stream of", b" Crypto Text"]; +/// let key = b"\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F"; +/// let iv = b"\x00\x01\x02\x03\x04\x05\x06\x07\x00\x01\x02\x03\x04\x05\x06\x07"; +/// let data_len = plaintexts.iter().fold(0, |sum, x| sum + x.len()); +/// +/// // Create a cipher context for encryption. +/// let mut encrypter = Crypter::new( +/// Cipher::aes_128_cbc(), +/// Mode::Encrypt, +/// key, +/// Some(iv)).unwrap(); +/// +/// let block_size = Cipher::aes_128_cbc().block_size(); +/// let mut ciphertext = vec![0; data_len + block_size]; +/// +/// // Encrypt 2 chunks of plaintexts successively. +/// let mut count = encrypter.update(plaintexts[0], &mut ciphertext).unwrap(); +/// count += encrypter.update(plaintexts[1], &mut ciphertext[count..]).unwrap(); +/// count += encrypter.finalize(&mut ciphertext[count..]).unwrap(); +/// ciphertext.truncate(count); +/// +/// assert_eq!( +/// b"\x0F\x21\x83\x7E\xB2\x88\x04\xAF\xD9\xCC\xE2\x03\x49\xB4\x88\xF6\xC4\x61\x0E\x32\x1C\xF9\ +/// \x0D\x66\xB1\xE6\x2C\x77\x76\x18\x8D\x99", +/// &ciphertext[..] +/// ); +/// +/// +/// // Let's pretend we don't know the plaintext, and now decrypt the ciphertext. +/// let data_len = ciphertext.len(); +/// let ciphertexts = [&ciphertext[..9], &ciphertext[9..]]; +/// +/// // Create a cipher context for decryption. +/// let mut decrypter = Crypter::new( +/// Cipher::aes_128_cbc(), +/// Mode::Decrypt, +/// key, +/// Some(iv)).unwrap(); +/// let mut plaintext = vec![0; data_len + block_size]; +/// +/// // Decrypt 2 chunks of ciphertexts successively. +/// let mut count = decrypter.update(ciphertexts[0], &mut plaintext).unwrap(); +/// count += decrypter.update(ciphertexts[1], &mut plaintext[count..]).unwrap(); +/// count += decrypter.finalize(&mut plaintext[count..]).unwrap(); +/// plaintext.truncate(count); +/// +/// assert_eq!(b"Some Stream of Crypto Text", &plaintext[..]); +/// ``` +pub struct Crypter { + ctx: *mut ffi::EVP_CIPHER_CTX, + block_size: usize, +} + +unsafe impl Sync for Crypter {} +unsafe impl Send for Crypter {} + +impl Crypter { + /// Creates a new `Crypter`. The initialisation vector, `iv`, is not necesarry for certain + /// types of `Cipher`. + /// + /// # Panics + /// + /// Panics if an IV is required by the cipher but not provided. Also make sure that the key + /// and IV size are appropriate for your cipher. + pub fn new( + t: Cipher, + mode: Mode, + key: &[u8], + iv: Option<&[u8]>, + ) -> Result { + ffi::init(); + + unsafe { + let ctx = cvt_p(ffi::EVP_CIPHER_CTX_new())?; + let crypter = Crypter { + ctx, + block_size: t.block_size(), + }; + + let mode = match mode { + Mode::Encrypt => 1, + Mode::Decrypt => 0, + }; + + cvt(ffi::EVP_CipherInit_ex( + crypter.ctx, + t.as_ptr(), + ptr::null_mut(), + ptr::null_mut(), + ptr::null_mut(), + mode, + ))?; + + assert!(key.len() <= c_int::max_value() as usize); + cvt(ffi::EVP_CIPHER_CTX_set_key_length( + crypter.ctx, + key.len() as c_uint, + ))?; + + let key = key.as_ptr() as *mut _; + let iv = match (iv, t.iv_len()) { + (Some(iv), Some(len)) => { + if iv.len() != len { + assert!(iv.len() <= c_int::max_value() as usize); + cvt(ffi::EVP_CIPHER_CTX_ctrl( + crypter.ctx, + ffi::EVP_CTRL_GCM_SET_IVLEN, + iv.len() as c_int, + ptr::null_mut(), + ))?; + } + iv.as_ptr() as *mut _ + } + (Some(_), None) | (None, None) => ptr::null_mut(), + (None, Some(_)) => panic!("an IV is required for this cipher"), + }; + cvt(ffi::EVP_CipherInit_ex( + crypter.ctx, + ptr::null(), + ptr::null_mut(), + key, + iv, + mode, + ))?; + + Ok(crypter) + } + } + + /// Enables or disables padding. + /// + /// If padding is disabled, total amount of data encrypted/decrypted must + /// be a multiple of the cipher's block size. + pub fn pad(&mut self, padding: bool) { + unsafe { + ffi::EVP_CIPHER_CTX_set_padding(self.ctx, padding as c_int); + } + } + + /// Sets the tag used to authenticate ciphertext in AEAD ciphers such as AES GCM. + /// + /// When decrypting cipher text using an AEAD cipher, this must be called before `finalize`. + pub fn set_tag(&mut self, tag: &[u8]) -> Result<(), ErrorStack> { + unsafe { + assert!(tag.len() <= c_int::max_value() as usize); + // NB: this constant is actually more general than just GCM. + cvt(ffi::EVP_CIPHER_CTX_ctrl( + self.ctx, + ffi::EVP_CTRL_GCM_SET_TAG, + tag.len() as c_int, + tag.as_ptr() as *mut _, + )) + .map(|_| ()) + } + } + + /// Sets the length of the authentication tag to generate in AES CCM. + /// + /// When encrypting with AES CCM, the tag length needs to be explicitly set in order + /// to use a value different than the default 12 bytes. + pub fn set_tag_len(&mut self, tag_len: usize) -> Result<(), ErrorStack> { + unsafe { + assert!(tag_len <= c_int::max_value() as usize); + // NB: this constant is actually more general than just GCM. + cvt(ffi::EVP_CIPHER_CTX_ctrl( + self.ctx, + ffi::EVP_CTRL_GCM_SET_TAG, + tag_len as c_int, + ptr::null_mut(), + )) + .map(|_| ()) + } + } + + /// Feeds total plaintext length to the cipher. + /// + /// The total plaintext or ciphertext length MUST be passed to the cipher when it operates in + /// CCM mode. + pub fn set_data_len(&mut self, data_len: usize) -> Result<(), ErrorStack> { + unsafe { + assert!(data_len <= c_int::max_value() as usize); + let mut len = 0; + cvt(ffi::EVP_CipherUpdate( + self.ctx, + ptr::null_mut(), + &mut len, + ptr::null_mut(), + data_len as c_int, + )) + .map(|_| ()) + } + } + + /// Feeds Additional Authenticated Data (AAD) through the cipher. + /// + /// This can only be used with AEAD ciphers such as AES GCM. Data fed in is not encrypted, but + /// is factored into the authentication tag. It must be called before the first call to + /// `update`. + pub fn aad_update(&mut self, input: &[u8]) -> Result<(), ErrorStack> { + unsafe { + assert!(input.len() <= c_int::max_value() as usize); + let mut len = 0; + cvt(ffi::EVP_CipherUpdate( + self.ctx, + ptr::null_mut(), + &mut len, + input.as_ptr(), + input.len() as c_int, + )) + .map(|_| ()) + } + } + + /// Feeds data from `input` through the cipher, writing encrypted/decrypted + /// bytes into `output`. + /// + /// The number of bytes written to `output` is returned. Note that this may + /// not be equal to the length of `input`. + /// + /// # Panics + /// + /// Panics for stream ciphers if `output.len() < input.len()`. + /// + /// Panics for block ciphers if `output.len() < input.len() + block_size`, + /// where `block_size` is the block size of the cipher (see `Cipher::block_size`). + /// + /// Panics if `output.len() > c_int::max_value()`. + pub fn update(&mut self, input: &[u8], output: &mut [u8]) -> Result { + unsafe { + let block_size = if self.block_size > 1 { + self.block_size + } else { + 0 + }; + assert!(output.len() >= input.len() + block_size); + assert!(output.len() <= c_int::max_value() as usize); + let mut outl = output.len() as c_int; + let inl = input.len() as c_int; + + cvt(ffi::EVP_CipherUpdate( + self.ctx, + output.as_mut_ptr(), + &mut outl, + input.as_ptr(), + inl, + ))?; + + Ok(outl as usize) + } + } + + /// Finishes the encryption/decryption process, writing any remaining data + /// to `output`. + /// + /// The number of bytes written to `output` is returned. + /// + /// `update` should not be called after this method. + /// + /// # Panics + /// + /// Panics for block ciphers if `output.len() < block_size`, + /// where `block_size` is the block size of the cipher (see `Cipher::block_size`). + pub fn finalize(&mut self, output: &mut [u8]) -> Result { + unsafe { + if self.block_size > 1 { + assert!(output.len() >= self.block_size); + } + let mut outl = cmp::min(output.len(), c_int::max_value() as usize) as c_int; + + cvt(ffi::EVP_CipherFinal_ex( + self.ctx, + output.as_mut_ptr(), + &mut outl, + ))?; + + Ok(outl as usize) + } + } + + /// Retrieves the authentication tag used to authenticate ciphertext in AEAD ciphers such + /// as AES GCM. + /// + /// When encrypting data with an AEAD cipher, this must be called after `finalize`. + /// + /// The size of the buffer indicates the required size of the tag. While some ciphers support a + /// range of tag sizes, it is recommended to pick the maximum size. For AES GCM, this is 16 + /// bytes, for example. + pub fn get_tag(&self, tag: &mut [u8]) -> Result<(), ErrorStack> { + unsafe { + assert!(tag.len() <= c_int::max_value() as usize); + cvt(ffi::EVP_CIPHER_CTX_ctrl( + self.ctx, + ffi::EVP_CTRL_GCM_GET_TAG, + tag.len() as c_int, + tag.as_mut_ptr() as *mut _, + )) + .map(|_| ()) + } + } +} + +impl Drop for Crypter { + fn drop(&mut self) { + unsafe { + ffi::EVP_CIPHER_CTX_free(self.ctx); + } + } +} + +/// Encrypts data in one go, and returns the encrypted data. +/// +/// Data is encrypted using the specified cipher type `t` in encrypt mode with the specified `key` +/// and initailization vector `iv`. Padding is enabled. +/// +/// This is a convenient interface to `Crypter` to encrypt all data in one go. To encrypt a stream +/// of data increamentally , use `Crypter` instead. +/// +/// # Examples +/// +/// Encrypt data in AES128 CBC mode +/// +/// ``` +/// use boring::symm::{encrypt, Cipher}; +/// +/// let cipher = Cipher::aes_128_cbc(); +/// let data = b"Some Crypto Text"; +/// let key = b"\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F"; +/// let iv = b"\x00\x01\x02\x03\x04\x05\x06\x07\x00\x01\x02\x03\x04\x05\x06\x07"; +/// let ciphertext = encrypt( +/// cipher, +/// key, +/// Some(iv), +/// data).unwrap(); +/// +/// assert_eq!( +/// b"\xB4\xB9\xE7\x30\xD6\xD6\xF7\xDE\x77\x3F\x1C\xFF\xB3\x3E\x44\x5A\x91\xD7\x27\x62\x87\x4D\ +/// \xFB\x3C\x5E\xC4\x59\x72\x4A\xF4\x7C\xA1", +/// &ciphertext[..]); +/// ``` +pub fn encrypt( + t: Cipher, + key: &[u8], + iv: Option<&[u8]>, + data: &[u8], +) -> Result, ErrorStack> { + cipher(t, Mode::Encrypt, key, iv, data) +} + +/// Decrypts data in one go, and returns the decrypted data. +/// +/// Data is decrypted using the specified cipher type `t` in decrypt mode with the specified `key` +/// and initailization vector `iv`. Padding is enabled. +/// +/// This is a convenient interface to `Crypter` to decrypt all data in one go. To decrypt a stream +/// of data increamentally , use `Crypter` instead. +/// +/// # Examples +/// +/// Decrypt data in AES128 CBC mode +/// +/// ``` +/// use boring::symm::{decrypt, Cipher}; +/// +/// let cipher = Cipher::aes_128_cbc(); +/// let data = b"\xB4\xB9\xE7\x30\xD6\xD6\xF7\xDE\x77\x3F\x1C\xFF\xB3\x3E\x44\x5A\x91\xD7\x27\x62\ +/// \x87\x4D\xFB\x3C\x5E\xC4\x59\x72\x4A\xF4\x7C\xA1"; +/// let key = b"\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F"; +/// let iv = b"\x00\x01\x02\x03\x04\x05\x06\x07\x00\x01\x02\x03\x04\x05\x06\x07"; +/// let ciphertext = decrypt( +/// cipher, +/// key, +/// Some(iv), +/// data).unwrap(); +/// +/// assert_eq!( +/// b"Some Crypto Text", +/// &ciphertext[..]); +/// ``` +pub fn decrypt( + t: Cipher, + key: &[u8], + iv: Option<&[u8]>, + data: &[u8], +) -> Result, ErrorStack> { + cipher(t, Mode::Decrypt, key, iv, data) +} + +fn cipher( + t: Cipher, + mode: Mode, + key: &[u8], + iv: Option<&[u8]>, + data: &[u8], +) -> Result, ErrorStack> { + let mut c = Crypter::new(t, mode, key, iv)?; + let mut out = vec![0; data.len() + t.block_size()]; + let count = c.update(data, &mut out)?; + let rest = c.finalize(&mut out[count..])?; + out.truncate(count + rest); + Ok(out) +} + +/// Like `encrypt`, but for AEAD ciphers such as AES GCM. +/// +/// Additional Authenticated Data can be provided in the `aad` field, and the authentication tag +/// will be copied into the `tag` field. +/// +/// The size of the `tag` buffer indicates the required size of the tag. While some ciphers support +/// a range of tag sizes, it is recommended to pick the maximum size. For AES GCM, this is 16 bytes, +/// for example. +pub fn encrypt_aead( + t: Cipher, + key: &[u8], + iv: Option<&[u8]>, + aad: &[u8], + data: &[u8], + tag: &mut [u8], +) -> Result, ErrorStack> { + let mut c = Crypter::new(t, Mode::Encrypt, key, iv)?; + let mut out = vec![0; data.len() + t.block_size()]; + + c.aad_update(aad)?; + let count = c.update(data, &mut out)?; + let rest = c.finalize(&mut out[count..])?; + c.get_tag(tag)?; + out.truncate(count + rest); + Ok(out) +} + +/// Like `decrypt`, but for AEAD ciphers such as AES GCM. +/// +/// Additional Authenticated Data can be provided in the `aad` field, and the authentication tag +/// should be provided in the `tag` field. +pub fn decrypt_aead( + t: Cipher, + key: &[u8], + iv: Option<&[u8]>, + aad: &[u8], + data: &[u8], + tag: &[u8], +) -> Result, ErrorStack> { + let mut c = Crypter::new(t, Mode::Decrypt, key, iv)?; + let mut out = vec![0; data.len() + t.block_size()]; + + c.aad_update(aad)?; + let count = c.update(data, &mut out)?; + + c.set_tag(tag)?; + let rest = c.finalize(&mut out[count..])?; + + out.truncate(count + rest); + Ok(out) +} + +use crate::ffi::{EVP_CIPHER_block_size, EVP_CIPHER_iv_length, EVP_CIPHER_key_length}; + +#[cfg(test)] +mod tests { + use super::*; + use hex::{self, FromHex}; + + #[test] + fn test_stream_cipher_output() { + let key = [0u8; 16]; + let iv = [0u8; 16]; + let mut c = super::Crypter::new( + super::Cipher::aes_128_ctr(), + super::Mode::Encrypt, + &key, + Some(&iv), + ) + .unwrap(); + + assert_eq!(c.update(&[0u8; 15], &mut [0u8; 15]).unwrap(), 15); + assert_eq!(c.update(&[0u8; 1], &mut [0u8; 1]).unwrap(), 1); + assert_eq!(c.finalize(&mut [0u8; 0]).unwrap(), 0); + } + + // Test vectors from FIPS-197: + // http://csrc.nist.gov/publications/fips/fips197/fips-197.pdf + #[test] + fn test_aes_256_ecb() { + let k0 = [ + 0x00u8, 0x01u8, 0x02u8, 0x03u8, 0x04u8, 0x05u8, 0x06u8, 0x07u8, 0x08u8, 0x09u8, 0x0au8, + 0x0bu8, 0x0cu8, 0x0du8, 0x0eu8, 0x0fu8, 0x10u8, 0x11u8, 0x12u8, 0x13u8, 0x14u8, 0x15u8, + 0x16u8, 0x17u8, 0x18u8, 0x19u8, 0x1au8, 0x1bu8, 0x1cu8, 0x1du8, 0x1eu8, 0x1fu8, + ]; + let p0 = [ + 0x00u8, 0x11u8, 0x22u8, 0x33u8, 0x44u8, 0x55u8, 0x66u8, 0x77u8, 0x88u8, 0x99u8, 0xaau8, + 0xbbu8, 0xccu8, 0xddu8, 0xeeu8, 0xffu8, + ]; + let c0 = [ + 0x8eu8, 0xa2u8, 0xb7u8, 0xcau8, 0x51u8, 0x67u8, 0x45u8, 0xbfu8, 0xeau8, 0xfcu8, 0x49u8, + 0x90u8, 0x4bu8, 0x49u8, 0x60u8, 0x89u8, + ]; + let mut c = super::Crypter::new( + super::Cipher::aes_256_ecb(), + super::Mode::Encrypt, + &k0, + None, + ) + .unwrap(); + c.pad(false); + let mut r0 = vec![0; c0.len() + super::Cipher::aes_256_ecb().block_size()]; + let count = c.update(&p0, &mut r0).unwrap(); + let rest = c.finalize(&mut r0[count..]).unwrap(); + r0.truncate(count + rest); + assert_eq!(hex::encode(&r0), hex::encode(c0)); + + let mut c = super::Crypter::new( + super::Cipher::aes_256_ecb(), + super::Mode::Decrypt, + &k0, + None, + ) + .unwrap(); + c.pad(false); + let mut p1 = vec![0; r0.len() + super::Cipher::aes_256_ecb().block_size()]; + let count = c.update(&r0, &mut p1).unwrap(); + let rest = c.finalize(&mut p1[count..]).unwrap(); + p1.truncate(count + rest); + assert_eq!(hex::encode(p1), hex::encode(p0)); + } + + #[test] + fn test_aes_256_cbc_decrypt() { + let iv = [ + 4_u8, 223_u8, 153_u8, 219_u8, 28_u8, 142_u8, 234_u8, 68_u8, 227_u8, 69_u8, 98_u8, + 107_u8, 208_u8, 14_u8, 236_u8, 60_u8, + ]; + let data = [ + 143_u8, 210_u8, 75_u8, 63_u8, 214_u8, 179_u8, 155_u8, 241_u8, 242_u8, 31_u8, 154_u8, + 56_u8, 198_u8, 145_u8, 192_u8, 64_u8, 2_u8, 245_u8, 167_u8, 220_u8, 55_u8, 119_u8, + 233_u8, 136_u8, 139_u8, 27_u8, 71_u8, 242_u8, 119_u8, 175_u8, 65_u8, 207_u8, + ]; + let ciphered_data = [ + 0x4a_u8, 0x2e_u8, 0xe5_u8, 0x6_u8, 0xbf_u8, 0xcf_u8, 0xf2_u8, 0xd7_u8, 0xea_u8, + 0x2d_u8, 0xb1_u8, 0x85_u8, 0x6c_u8, 0x93_u8, 0x65_u8, 0x6f_u8, + ]; + let mut cr = super::Crypter::new( + super::Cipher::aes_256_cbc(), + super::Mode::Decrypt, + &data, + Some(&iv), + ) + .unwrap(); + cr.pad(false); + let mut unciphered_data = vec![0; data.len() + super::Cipher::aes_256_cbc().block_size()]; + let count = cr.update(&ciphered_data, &mut unciphered_data).unwrap(); + let rest = cr.finalize(&mut unciphered_data[count..]).unwrap(); + unciphered_data.truncate(count + rest); + + let expected_unciphered_data = b"I love turtles.\x01"; + + assert_eq!(&unciphered_data, expected_unciphered_data); + } + + fn cipher_test(ciphertype: super::Cipher, pt: &str, ct: &str, key: &str, iv: &str) { + let pt = Vec::from_hex(pt).unwrap(); + let ct = Vec::from_hex(ct).unwrap(); + let key = Vec::from_hex(key).unwrap(); + let iv = Vec::from_hex(iv).unwrap(); + + let computed = super::decrypt(ciphertype, &key, Some(&iv), &ct).unwrap(); + let expected = pt; + + if computed != expected { + println!("Computed: {}", hex::encode(&computed)); + println!("Expected: {}", hex::encode(&expected)); + if computed.len() != expected.len() { + println!( + "Lengths differ: {} in computed vs {} expected", + computed.len(), + expected.len() + ); + } + panic!("test failure"); + } + } + + #[test] + fn test_rc4() { + let pt = "0000000000000000000000000000000000000000000000000000000000000000000000000000"; + let ct = "A68686B04D686AA107BD8D4CAB191A3EEC0A6294BC78B60F65C25CB47BD7BB3A48EFC4D26BE4"; + let key = "97CD440324DA5FD1F7955C1C13B6B466"; + let iv = ""; + + cipher_test(super::Cipher::rc4(), pt, ct, key, iv); + } + + #[test] + fn test_aes128_ctr() { + let pt = "6BC1BEE22E409F96E93D7E117393172AAE2D8A571E03AC9C9EB76FAC45AF8E5130C81C46A35CE411\ + E5FBC1191A0A52EFF69F2445DF4F9B17AD2B417BE66C3710"; + let ct = "874D6191B620E3261BEF6864990DB6CE9806F66B7970FDFF8617187BB9FFFDFF5AE4DF3EDBD5D35E\ + 5B4F09020DB03EAB1E031DDA2FBE03D1792170A0F3009CEE"; + let key = "2B7E151628AED2A6ABF7158809CF4F3C"; + let iv = "F0F1F2F3F4F5F6F7F8F9FAFBFCFDFEFF"; + + cipher_test(super::Cipher::aes_128_ctr(), pt, ct, key, iv); + } + + #[test] + fn test_aes128_ofb() { + // Lifted from http://csrc.nist.gov/publications/nistpubs/800-38a/sp800-38a.pdf + + let pt = "6bc1bee22e409f96e93d7e117393172aae2d8a571e03ac9c9eb76fac45af8e5130c81c46a35ce411e5fbc1191a0a52eff69f2445df4f9b17ad2b417be66c3710"; + let ct = "3b3fd92eb72dad20333449f8e83cfb4a7789508d16918f03f53c52dac54ed8259740051e9c5fecf64344f7a82260edcc304c6528f659c77866a510d9c1d6ae5e"; + let key = "2b7e151628aed2a6abf7158809cf4f3c"; + let iv = "000102030405060708090a0b0c0d0e0f"; + + cipher_test(super::Cipher::aes_128_ofb(), pt, ct, key, iv); + } + + #[test] + fn test_aes192_ctr() { + // Lifted from http://csrc.nist.gov/publications/nistpubs/800-38a/sp800-38a.pdf + + let pt = "6bc1bee22e409f96e93d7e117393172aae2d8a571e03ac9c9eb76fac45af8e5130c81c46a35ce411e5fbc1191a0a52eff69f2445df4f9b17ad2b417be66c3710"; + let ct = "1abc932417521ca24f2b0459fe7e6e0b090339ec0aa6faefd5ccc2c6f4ce8e941e36b26bd1ebc670d1bd1d665620abf74f78a7f6d29809585a97daec58c6b050"; + let key = "8e73b0f7da0e6452c810f32b809079e562f8ead2522c6b7b"; + let iv = "f0f1f2f3f4f5f6f7f8f9fafbfcfdfeff"; + + cipher_test(super::Cipher::aes_192_ctr(), pt, ct, key, iv); + } + + #[test] + fn test_aes192_ofb() { + // Lifted from http://csrc.nist.gov/publications/nistpubs/800-38a/sp800-38a.pdf + + let pt = "6bc1bee22e409f96e93d7e117393172aae2d8a571e03ac9c9eb76fac45af8e5130c81c46a35ce411e5fbc1191a0a52eff69f2445df4f9b17ad2b417be66c3710"; + let ct = "cdc80d6fddf18cab34c25909c99a4174fcc28b8d4c63837c09e81700c11004018d9a9aeac0f6596f559c6d4daf59a5f26d9f200857ca6c3e9cac524bd9acc92a"; + let key = "8e73b0f7da0e6452c810f32b809079e562f8ead2522c6b7b"; + let iv = "000102030405060708090a0b0c0d0e0f"; + + cipher_test(super::Cipher::aes_192_ofb(), pt, ct, key, iv); + } + + #[test] + fn test_aes256_ofb() { + // Lifted from http://csrc.nist.gov/publications/nistpubs/800-38a/sp800-38a.pdf + + let pt = "6bc1bee22e409f96e93d7e117393172aae2d8a571e03ac9c9eb76fac45af8e5130c81c46a35ce411e5fbc1191a0a52eff69f2445df4f9b17ad2b417be66c3710"; + let ct = "dc7e84bfda79164b7ecd8486985d38604febdc6740d20b3ac88f6ad82a4fb08d71ab47a086e86eedf39d1c5bba97c4080126141d67f37be8538f5a8be740e484"; + let key = "603deb1015ca71be2b73aef0857d77811f352c073b6108d72d9810a30914dff4"; + let iv = "000102030405060708090a0b0c0d0e0f"; + + cipher_test(super::Cipher::aes_256_ofb(), pt, ct, key, iv); + } + + #[test] + fn test_des_cbc() { + let pt = "54686973206973206120746573742e"; + let ct = "6f2867cfefda048a4046ef7e556c7132"; + let key = "7cb66337f3d3c0fe"; + let iv = "0001020304050607"; + + cipher_test(super::Cipher::des_cbc(), pt, ct, key, iv); + } + + #[test] + fn test_des_ecb() { + let pt = "54686973206973206120746573742e"; + let ct = "0050ab8aecec758843fe157b4dde938c"; + let key = "7cb66337f3d3c0fe"; + let iv = "0001020304050607"; + + cipher_test(super::Cipher::des_ecb(), pt, ct, key, iv); + } + + #[test] + fn test_des_ede3() { + let pt = "9994f4c69d40ae4f34ff403b5cf39d4c8207ea5d3e19a5fd"; + let ct = "9e5c4297d60582f81071ac8ab7d0698d4c79de8b94c519858207ea5d3e19a5fd"; + let key = "010203040506070801020304050607080102030405060708"; + let iv = "5cc118306dc702e4"; + + cipher_test(super::Cipher::des_ede3(), pt, ct, key, iv); + } + + #[test] + fn test_des_ede3_cbc() { + let pt = "54686973206973206120746573742e"; + let ct = "6f2867cfefda048a4046ef7e556c7132"; + let key = "7cb66337f3d3c0fe7cb66337f3d3c0fe7cb66337f3d3c0fe"; + let iv = "0001020304050607"; + + cipher_test(super::Cipher::des_ede3_cbc(), pt, ct, key, iv); + } + + #[test] + fn test_aes128_gcm() { + let key = "0e00c76561d2bd9b40c3c15427e2b08f"; + let iv = "492cadaccd3ca3fbc9cf9f06eb3325c4e159850b0dbe98199b89b7af528806610b6f63998e1eae80c348e7\ + 4cbb921d8326631631fc6a5d304f39166daf7ea15fa1977f101819adb510b50fe9932e12c5a85aa3fd1e73\ + d8d760af218be829903a77c63359d75edd91b4f6ed5465a72662f5055999e059e7654a8edc921aa0d496"; + let pt = "fef03c2d7fb15bf0d2df18007d99f967c878ad59359034f7bb2c19af120685d78e32f6b8b83b032019956c\ + a9c0195721476b85"; + let aad = "d8f1163d8c840292a2b2dacf4ac7c36aff8733f18fabb4fa5594544125e03d1e6e5d6d0fd61656c8d8f327\ + c92839ae5539bb469c9257f109ebff85aad7bd220fdaa95c022dbd0c7bb2d878ad504122c943045d3c5eba\ + 8f1f56c0"; + let ct = "4f6cf471be7cbd2575cd5a1747aea8fe9dea83e51936beac3e68f66206922060c697ffa7af80ad6bb68f2c\ + f4fc97416ee52abe"; + let tag = "e20b6655"; + + // this tag is smaller than you'd normally want, but I pulled this test from the part of + // the NIST test vectors that cover 4 byte tags. + let mut actual_tag = [0; 4]; + let out = encrypt_aead( + Cipher::aes_128_gcm(), + &Vec::from_hex(key).unwrap(), + Some(&Vec::from_hex(iv).unwrap()), + &Vec::from_hex(aad).unwrap(), + &Vec::from_hex(pt).unwrap(), + &mut actual_tag, + ) + .unwrap(); + assert_eq!(ct, hex::encode(out)); + assert_eq!(tag, hex::encode(actual_tag)); + + let out = decrypt_aead( + Cipher::aes_128_gcm(), + &Vec::from_hex(key).unwrap(), + Some(&Vec::from_hex(iv).unwrap()), + &Vec::from_hex(aad).unwrap(), + &Vec::from_hex(ct).unwrap(), + &Vec::from_hex(tag).unwrap(), + ) + .unwrap(); + assert_eq!(pt, hex::encode(out)); + } +} diff --git a/crates/boring/src/util.rs b/crates/boring/src/util.rs new file mode 100644 index 000000000..21591d2f4 --- /dev/null +++ b/crates/boring/src/util.rs @@ -0,0 +1,67 @@ +use libc::{c_char, c_int, c_void}; +use std::any::Any; +use std::panic::{self, AssertUnwindSafe}; +use std::slice; + +use crate::error::ErrorStack; + +/// Wraps a user-supplied callback and a slot for panics thrown inside the callback (while FFI +/// frames are on the stack). +/// +/// When dropped, checks if the callback has panicked, and resumes unwinding if so. +pub struct CallbackState { + /// The user callback. Taken out of the `Option` when called. + cb: Option, + /// If the callback panics, we place the panic object here, to be re-thrown once OpenSSL + /// returns. + panic: Option>, +} + +impl CallbackState { + pub fn new(callback: F) -> Self { + CallbackState { + cb: Some(callback), + panic: None, + } + } +} + +impl Drop for CallbackState { + fn drop(&mut self) { + if let Some(panic) = self.panic.take() { + panic::resume_unwind(panic); + } + } +} + +/// Password callback function, passed to private key loading functions. +/// +/// `cb_state` is expected to be a pointer to a `CallbackState`. +pub unsafe extern "C" fn invoke_passwd_cb( + buf: *mut c_char, + size: c_int, + _rwflag: c_int, + cb_state: *mut c_void, +) -> c_int +where + F: FnOnce(&mut [u8]) -> Result, +{ + let callback = &mut *(cb_state as *mut CallbackState); + + let result = panic::catch_unwind(AssertUnwindSafe(|| { + let pass_slice = slice::from_raw_parts_mut(buf as *mut u8, size as usize); + callback.cb.take().unwrap()(pass_slice) + })); + + match result { + Ok(Ok(len)) => len as c_int, + Ok(Err(_)) => { + // FIXME restore error stack + 0 + } + Err(err) => { + callback.panic = Some(err); + 0 + } + } +} diff --git a/crates/boring/src/version.rs b/crates/boring/src/version.rs new file mode 100644 index 000000000..821441bd6 --- /dev/null +++ b/crates/boring/src/version.rs @@ -0,0 +1,111 @@ +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +use std::ffi::CStr; + +use crate::ffi::{ + OpenSSL_version, OpenSSL_version_num, OPENSSL_BUILT_ON, OPENSSL_CFLAGS, OPENSSL_DIR, + OPENSSL_PLATFORM, OPENSSL_VERSION, +}; + +/// OPENSSL_VERSION_NUMBER is a numeric release version identifier: +/// +/// `MNNFFPPS: major minor fix patch status` +/// +/// The status nibble has one of the values 0 for development, 1 to e for betas 1 to 14, and f for release. +/// +/// for example +/// +/// `0x000906000 == 0.9.6 dev` +/// `0x000906023 == 0.9.6b beta 3` +/// `0x00090605f == 0.9.6e release` +/// +/// Versions prior to 0.9.3 have identifiers < 0x0930. Versions between 0.9.3 and 0.9.5 had a version identifier with this interpretation: +/// +/// `MMNNFFRBB major minor fix final beta/patch` +/// +/// for example +/// +/// `0x000904100 == 0.9.4 release` +/// `0x000905000 == 0.9.5 dev` +/// +/// Version 0.9.5a had an interim interpretation that is like the current one, except the patch level got the highest bit set, to keep continuity. The number was therefore 0x0090581f +/// +/// The return value of this function can be compared to the macro to make sure that the correct version of the library has been loaded, especially when using DLLs on Windows systems. +pub fn number() -> i64 { + unsafe { OpenSSL_version_num() as i64 } +} + +/// The text variant of the version number and the release date. For example, "OpenSSL 0.9.5a 1 Apr 2000". +pub fn version() -> &'static str { + unsafe { + CStr::from_ptr(OpenSSL_version(OPENSSL_VERSION)) + .to_str() + .unwrap() + } +} + +/// The compiler flags set for the compilation process in the form "compiler: ..." if available or +/// "compiler: information not available" otherwise. +pub fn c_flags() -> &'static str { + unsafe { + CStr::from_ptr(OpenSSL_version(OPENSSL_CFLAGS)) + .to_str() + .unwrap() + } +} + +/// The date of the build process in the form "built on: ..." if available or "built on: date not available" otherwise. +pub fn built_on() -> &'static str { + unsafe { + CStr::from_ptr(OpenSSL_version(OPENSSL_BUILT_ON)) + .to_str() + .unwrap() + } +} + +/// The "Configure" target of the library build in the form "platform: ..." if available or "platform: information not available" otherwise. +pub fn platform() -> &'static str { + unsafe { + CStr::from_ptr(OpenSSL_version(OPENSSL_PLATFORM)) + .to_str() + .unwrap() + } +} + +/// The "OPENSSLDIR" setting of the library build in the form "OPENSSLDIR: "..."" if available or "OPENSSLDIR: N/A" otherwise. +pub fn dir() -> &'static str { + unsafe { + CStr::from_ptr(OpenSSL_version(OPENSSL_DIR)) + .to_str() + .unwrap() + } +} + +/// This test ensures that we do not segfault when calling the functions of this module +/// and that the strings respect a reasonable format. +#[test] +fn test_versions() { + println!("Number: '{}'", number()); + println!("Version: '{}'", version()); + println!("C flags: '{}'", c_flags()); + println!("Built on: '{}'", built_on()); + println!("Platform: '{}'", platform()); + println!("Dir: '{}'", dir()); + + assert!(number() > 0); + assert!(version().starts_with("BoringSSL")); + assert!(c_flags().starts_with("compiler:")); + assert!(built_on().starts_with("built on:")); + assert!(dir().starts_with("OPENSSLDIR:")); +} diff --git a/crates/boring/src/x509/extension.rs b/crates/boring/src/x509/extension.rs new file mode 100644 index 000000000..639d28bd2 --- /dev/null +++ b/crates/boring/src/x509/extension.rs @@ -0,0 +1,531 @@ +//! Add extensions to an `X509` certificate or certificate request. +//! +//! The extensions defined for X.509 v3 certificates provide methods for +//! associating additional attributes with users or public keys and for +//! managing relationships between CAs. The extensions created using this +//! module can be used with `X509v3Context` objects. +//! +//! # Example +//! +//! ```rust +//! use boring::x509::extension::BasicConstraints; +//! use boring::x509::X509Extension; +//! +//! let mut bc = BasicConstraints::new(); +//! let bc = bc.critical().ca().pathlen(1); +//! let extension: X509Extension = bc.build().unwrap(); +//! ``` +use std::fmt::Write; + +use crate::asn1::Asn1Object; +use crate::error::ErrorStack; +use crate::nid::Nid; +use crate::x509::{GeneralName, Stack, X509Extension, X509v3Context}; +use foreign_types::ForeignType; + +/// An extension which indicates whether a certificate is a CA certificate. +pub struct BasicConstraints { + critical: bool, + ca: bool, + pathlen: Option, +} + +impl Default for BasicConstraints { + fn default() -> BasicConstraints { + BasicConstraints::new() + } +} + +impl BasicConstraints { + /// Construct a new `BasicConstraints` extension. + pub fn new() -> BasicConstraints { + BasicConstraints { + critical: false, + ca: false, + pathlen: None, + } + } + + /// Sets the `critical` flag to `true`. The extension will be critical. + pub fn critical(&mut self) -> &mut BasicConstraints { + self.critical = true; + self + } + + /// Sets the `ca` flag to `true`. + pub fn ca(&mut self) -> &mut BasicConstraints { + self.ca = true; + self + } + + /// Sets the pathlen to an optional non-negative value. The pathlen is the + /// maximum number of CAs that can appear below this one in a chain. + pub fn pathlen(&mut self, pathlen: u32) -> &mut BasicConstraints { + self.pathlen = Some(pathlen); + self + } + + /// Return the `BasicConstraints` extension as an `X509Extension`. + pub fn build(&self) -> Result { + let mut value = String::new(); + if self.critical { + value.push_str("critical,"); + } + value.push_str("CA:"); + if self.ca { + value.push_str("TRUE"); + } else { + value.push_str("FALSE"); + } + if let Some(pathlen) = self.pathlen { + write!(value, ",pathlen:{}", pathlen).unwrap(); + } + X509Extension::new_nid(None, None, Nid::BASIC_CONSTRAINTS, &value) + } +} + +/// An extension consisting of a list of names of the permitted key usages. +pub struct KeyUsage { + critical: bool, + digital_signature: bool, + non_repudiation: bool, + key_encipherment: bool, + data_encipherment: bool, + key_agreement: bool, + key_cert_sign: bool, + crl_sign: bool, + encipher_only: bool, + decipher_only: bool, +} + +impl Default for KeyUsage { + fn default() -> KeyUsage { + KeyUsage::new() + } +} + +impl KeyUsage { + /// Construct a new `KeyUsage` extension. + pub fn new() -> KeyUsage { + KeyUsage { + critical: false, + digital_signature: false, + non_repudiation: false, + key_encipherment: false, + data_encipherment: false, + key_agreement: false, + key_cert_sign: false, + crl_sign: false, + encipher_only: false, + decipher_only: false, + } + } + + /// Sets the `critical` flag to `true`. The extension will be critical. + pub fn critical(&mut self) -> &mut KeyUsage { + self.critical = true; + self + } + + /// Sets the `digitalSignature` flag to `true`. + pub fn digital_signature(&mut self) -> &mut KeyUsage { + self.digital_signature = true; + self + } + + /// Sets the `nonRepudiation` flag to `true`. + pub fn non_repudiation(&mut self) -> &mut KeyUsage { + self.non_repudiation = true; + self + } + + /// Sets the `keyEncipherment` flag to `true`. + pub fn key_encipherment(&mut self) -> &mut KeyUsage { + self.key_encipherment = true; + self + } + + /// Sets the `dataEncipherment` flag to `true`. + pub fn data_encipherment(&mut self) -> &mut KeyUsage { + self.data_encipherment = true; + self + } + + /// Sets the `keyAgreement` flag to `true`. + pub fn key_agreement(&mut self) -> &mut KeyUsage { + self.key_agreement = true; + self + } + + /// Sets the `keyCertSign` flag to `true`. + pub fn key_cert_sign(&mut self) -> &mut KeyUsage { + self.key_cert_sign = true; + self + } + + /// Sets the `cRLSign` flag to `true`. + pub fn crl_sign(&mut self) -> &mut KeyUsage { + self.crl_sign = true; + self + } + + /// Sets the `encipherOnly` flag to `true`. + pub fn encipher_only(&mut self) -> &mut KeyUsage { + self.encipher_only = true; + self + } + + /// Sets the `decipherOnly` flag to `true`. + pub fn decipher_only(&mut self) -> &mut KeyUsage { + self.decipher_only = true; + self + } + + /// Return the `KeyUsage` extension as an `X509Extension`. + pub fn build(&self) -> Result { + let mut value = String::new(); + let mut first = true; + append(&mut value, &mut first, self.critical, "critical"); + append( + &mut value, + &mut first, + self.digital_signature, + "digitalSignature", + ); + append( + &mut value, + &mut first, + self.non_repudiation, + "nonRepudiation", + ); + append( + &mut value, + &mut first, + self.key_encipherment, + "keyEncipherment", + ); + append( + &mut value, + &mut first, + self.data_encipherment, + "dataEncipherment", + ); + append(&mut value, &mut first, self.key_agreement, "keyAgreement"); + append(&mut value, &mut first, self.key_cert_sign, "keyCertSign"); + append(&mut value, &mut first, self.crl_sign, "cRLSign"); + append(&mut value, &mut first, self.encipher_only, "encipherOnly"); + append(&mut value, &mut first, self.decipher_only, "decipherOnly"); + X509Extension::new_nid(None, None, Nid::KEY_USAGE, &value) + } +} + +/// An extension consisting of a list of usages indicating purposes +/// for which the certificate public key can be used for. +pub struct ExtendedKeyUsage { + critical: bool, + items: Vec, +} + +impl Default for ExtendedKeyUsage { + fn default() -> ExtendedKeyUsage { + ExtendedKeyUsage::new() + } +} + +impl ExtendedKeyUsage { + /// Construct a new `ExtendedKeyUsage` extension. + pub fn new() -> ExtendedKeyUsage { + ExtendedKeyUsage { + critical: false, + items: vec![], + } + } + + /// Sets the `critical` flag to `true`. The extension will be critical. + pub fn critical(&mut self) -> &mut ExtendedKeyUsage { + self.critical = true; + self + } + + /// Sets the `serverAuth` flag to `true`. + pub fn server_auth(&mut self) -> &mut ExtendedKeyUsage { + self.other("serverAuth") + } + + /// Sets the `clientAuth` flag to `true`. + pub fn client_auth(&mut self) -> &mut ExtendedKeyUsage { + self.other("clientAuth") + } + + /// Sets the `codeSigning` flag to `true`. + pub fn code_signing(&mut self) -> &mut ExtendedKeyUsage { + self.other("codeSigning") + } + + /// Sets the `timeStamping` flag to `true`. + pub fn time_stamping(&mut self) -> &mut ExtendedKeyUsage { + self.other("timeStamping") + } + + /// Sets the `msCodeInd` flag to `true`. + pub fn ms_code_ind(&mut self) -> &mut ExtendedKeyUsage { + self.other("msCodeInd") + } + + /// Sets the `msCodeCom` flag to `true`. + pub fn ms_code_com(&mut self) -> &mut ExtendedKeyUsage { + self.other("msCodeCom") + } + + /// Sets the `msCTLSign` flag to `true`. + pub fn ms_ctl_sign(&mut self) -> &mut ExtendedKeyUsage { + self.other("msCTLSign") + } + + /// Sets the `msSGC` flag to `true`. + pub fn ms_sgc(&mut self) -> &mut ExtendedKeyUsage { + self.other("msSGC") + } + + /// Sets the `msEFS` flag to `true`. + pub fn ms_efs(&mut self) -> &mut ExtendedKeyUsage { + self.other("msEFS") + } + + /// Sets the `nsSGC` flag to `true`. + pub fn ns_sgc(&mut self) -> &mut ExtendedKeyUsage { + self.other("nsSGC") + } + + /// Sets a flag not already defined. + pub fn other(&mut self, other: &str) -> &mut ExtendedKeyUsage { + self.items.push(other.to_string()); + self + } + + /// Return the `ExtendedKeyUsage` extension as an `X509Extension`. + pub fn build(&self) -> Result { + let mut stack = Stack::new()?; + for item in &self.items { + stack.push(Asn1Object::from_str(item)?)?; + } + unsafe { + X509Extension::new_internal(Nid::EXT_KEY_USAGE, self.critical, stack.as_ptr().cast()) + } + } +} + +/// An extension that provides a means of identifying certificates that contain a +/// particular public key. +pub struct SubjectKeyIdentifier { + critical: bool, +} + +impl Default for SubjectKeyIdentifier { + fn default() -> SubjectKeyIdentifier { + SubjectKeyIdentifier::new() + } +} + +impl SubjectKeyIdentifier { + /// Construct a new `SubjectKeyIdentifier` extension. + pub fn new() -> SubjectKeyIdentifier { + SubjectKeyIdentifier { critical: false } + } + + /// Sets the `critical` flag to `true`. The extension will be critical. + pub fn critical(&mut self) -> &mut SubjectKeyIdentifier { + self.critical = true; + self + } + + /// Return a `SubjectKeyIdentifier` extension as an `X509Extension`. + pub fn build(&self, ctx: &X509v3Context) -> Result { + let mut value = String::new(); + let mut first = true; + append(&mut value, &mut first, self.critical, "critical"); + append(&mut value, &mut first, true, "hash"); + X509Extension::new_nid(None, Some(ctx), Nid::SUBJECT_KEY_IDENTIFIER, &value) + } +} + +/// An extension that provides a means of identifying the public key corresponding +/// to the private key used to sign a CRL. +pub struct AuthorityKeyIdentifier { + critical: bool, + keyid: Option, + issuer: Option, +} + +impl Default for AuthorityKeyIdentifier { + fn default() -> AuthorityKeyIdentifier { + AuthorityKeyIdentifier::new() + } +} + +impl AuthorityKeyIdentifier { + /// Construct a new `AuthorityKeyIdentifier` extension. + pub fn new() -> AuthorityKeyIdentifier { + AuthorityKeyIdentifier { + critical: false, + keyid: None, + issuer: None, + } + } + + /// Sets the `critical` flag to `true`. The extension will be critical. + pub fn critical(&mut self) -> &mut AuthorityKeyIdentifier { + self.critical = true; + self + } + + /// Sets the `keyid` flag. + pub fn keyid(&mut self, always: bool) -> &mut AuthorityKeyIdentifier { + self.keyid = Some(always); + self + } + + /// Sets the `issuer` flag. + pub fn issuer(&mut self, always: bool) -> &mut AuthorityKeyIdentifier { + self.issuer = Some(always); + self + } + + /// Return a `AuthorityKeyIdentifier` extension as an `X509Extension`. + pub fn build(&self, ctx: &X509v3Context) -> Result { + let mut value = String::new(); + let mut first = true; + append(&mut value, &mut first, self.critical, "critical"); + match self.keyid { + Some(true) => append(&mut value, &mut first, true, "keyid:always"), + Some(false) => append(&mut value, &mut first, true, "keyid"), + None => {} + } + match self.issuer { + Some(true) => append(&mut value, &mut first, true, "issuer:always"), + Some(false) => append(&mut value, &mut first, true, "issuer"), + None => {} + } + X509Extension::new_nid(None, Some(ctx), Nid::AUTHORITY_KEY_IDENTIFIER, &value) + } +} + +enum RustGeneralName { + Dns(String), + Email(String), + Uri(String), + Ip(String), + Rid(String), +} + +/// An extension that allows additional identities to be bound to the subject +/// of the certificate. +pub struct SubjectAlternativeName { + critical: bool, + items: Vec, +} + +impl Default for SubjectAlternativeName { + fn default() -> SubjectAlternativeName { + SubjectAlternativeName::new() + } +} + +impl SubjectAlternativeName { + /// Construct a new `SubjectAlternativeName` extension. + pub fn new() -> SubjectAlternativeName { + SubjectAlternativeName { + critical: false, + items: vec![], + } + } + + /// Sets the `critical` flag to `true`. The extension will be critical. + pub fn critical(&mut self) -> &mut SubjectAlternativeName { + self.critical = true; + self + } + + /// Sets the `email` flag. + pub fn email(&mut self, email: &str) -> &mut SubjectAlternativeName { + self.items.push(RustGeneralName::Email(email.to_string())); + self + } + + /// Sets the `uri` flag. + pub fn uri(&mut self, uri: &str) -> &mut SubjectAlternativeName { + self.items.push(RustGeneralName::Uri(uri.to_string())); + self + } + + /// Sets the `dns` flag. + pub fn dns(&mut self, dns: &str) -> &mut SubjectAlternativeName { + self.items.push(RustGeneralName::Dns(dns.to_string())); + self + } + + /// Sets the `rid` flag. + pub fn rid(&mut self, rid: &str) -> &mut SubjectAlternativeName { + self.items.push(RustGeneralName::Rid(rid.to_string())); + self + } + + /// Sets the `ip` flag. + pub fn ip(&mut self, ip: &str) -> &mut SubjectAlternativeName { + self.items.push(RustGeneralName::Ip(ip.to_string())); + self + } + + /// Sets the `dirName` flag. + /// + /// Not currently actually supported, always panics. + #[deprecated = "dir_name is deprecated and always panics. Please file a bug if you have a use case for this."] + pub fn dir_name(&mut self, _dir_name: &str) -> &mut SubjectAlternativeName { + unimplemented!( + "This has not yet been adapted for the new internals. File a bug if you need this." + ); + } + + /// Sets the `otherName` flag. + /// + /// Not currently actually supported, always panics. + #[deprecated = "other_name is deprecated and always panics. Please file a bug if you have a use case for this."] + pub fn other_name(&mut self, _other_name: &str) -> &mut SubjectAlternativeName { + unimplemented!( + "This has not yet been adapted for the new internals. File a bug if you need this." + ); + } + + /// Return a `SubjectAlternativeName` extension as an `X509Extension`. + pub fn build(&self, _ctx: &X509v3Context<'_>) -> Result { + let mut stack = Stack::new()?; + for item in &self.items { + let gn = match item { + RustGeneralName::Dns(s) => GeneralName::new_dns(s.as_bytes())?, + RustGeneralName::Email(s) => GeneralName::new_email(s.as_bytes())?, + RustGeneralName::Uri(s) => GeneralName::new_uri(s.as_bytes())?, + RustGeneralName::Ip(s) => { + GeneralName::new_ip(s.parse().map_err(|_| ErrorStack::get())?)? + } + RustGeneralName::Rid(s) => GeneralName::new_rid(Asn1Object::from_str(s)?)?, + }; + stack.push(gn)?; + } + + unsafe { + X509Extension::new_internal(Nid::SUBJECT_ALT_NAME, self.critical, stack.as_ptr().cast()) + } + } +} + +fn append(value: &mut String, first: &mut bool, should: bool, element: &str) { + if !should { + return; + } + + if !*first { + value.push(','); + } + *first = false; + value.push_str(element); +} diff --git a/crates/boring/src/x509/mod.rs b/crates/boring/src/x509/mod.rs new file mode 100644 index 000000000..30b5e725b --- /dev/null +++ b/crates/boring/src/x509/mod.rs @@ -0,0 +1,1700 @@ +//! The standard defining the format of public key certificates. +//! +//! An `X509` certificate binds an identity to a public key, and is either +//! signed by a certificate authority (CA) or self-signed. An entity that gets +//! a hold of a certificate can both verify your identity (via a CA) and encrypt +//! data with the included public key. `X509` certificates are used in many +//! Internet protocols, including SSL/TLS, which is the basis for HTTPS, +//! the secure protocol for browsing the web. + +use crate::ffi; +use foreign_types::{ForeignType, ForeignTypeRef}; +use libc::{c_int, c_long, c_void}; +use std::convert::TryInto; +use std::error::Error; +use std::ffi::{CStr, CString}; +use std::fmt; +use std::marker::PhantomData; +use std::mem; +use std::net::IpAddr; +use std::path::Path; +use std::ptr; +use std::slice; +use std::str; + +use crate::asn1::{ + Asn1BitStringRef, Asn1IntegerRef, Asn1Object, Asn1ObjectRef, Asn1StringRef, Asn1TimeRef, + Asn1Type, +}; +use crate::bio::MemBioSlice; +use crate::conf::ConfRef; +use crate::error::ErrorStack; +use crate::ex_data::Index; +use crate::hash::{DigestBytes, MessageDigest}; +use crate::nid::Nid; +use crate::pkey::{HasPrivate, HasPublic, PKey, PKeyRef, Public}; +use crate::ssl::SslRef; +use crate::stack::{Stack, StackRef, Stackable}; +use crate::string::OpensslString; +use crate::{cvt, cvt_n, cvt_p}; + +pub mod extension; +pub mod store; +pub mod verify; + +#[cfg(test)] +mod tests; + +foreign_type_and_impl_send_sync! { + type CType = ffi::X509_STORE_CTX; + fn drop = ffi::X509_STORE_CTX_free; + + /// An `X509` certificate store context. + pub struct X509StoreContext; +} + +impl X509StoreContext { + /// Returns the index which can be used to obtain a reference to the `Ssl` associated with a + /// context. + pub fn ssl_idx() -> Result, ErrorStack> { + unsafe { cvt_n(ffi::SSL_get_ex_data_X509_STORE_CTX_idx()).map(|idx| Index::from_raw(idx)) } + } + + /// Creates a new `X509StoreContext` instance. + /// + /// This corresponds to [`X509_STORE_CTX_new`]. + /// + /// [`X509_STORE_CTX_new`]: https://www.openssl.org/docs/man1.1.0/crypto/X509_STORE_CTX_new.html + pub fn new() -> Result { + unsafe { + ffi::init(); + cvt_p(ffi::X509_STORE_CTX_new()).map(|p| X509StoreContext::from_ptr(p)) + } + } +} + +impl X509StoreContextRef { + /// Returns application data pertaining to an `X509` store context. + /// + /// This corresponds to [`X509_STORE_CTX_get_ex_data`]. + /// + /// [`X509_STORE_CTX_get_ex_data`]: https://www.openssl.org/docs/man1.0.2/crypto/X509_STORE_CTX_get_ex_data.html + pub fn ex_data(&self, index: Index) -> Option<&T> { + unsafe { + let data = ffi::X509_STORE_CTX_get_ex_data(self.as_ptr(), index.as_raw()); + if data.is_null() { + None + } else { + Some(&*(data as *const T)) + } + } + } + + /// Returns the verify result of the context. + /// + /// This corresponds to [`X509_STORE_CTX_get_error`]. + /// + /// [`X509_STORE_CTX_get_error`]: https://www.openssl.org/docs/man1.1.0/crypto/X509_STORE_CTX_get_error.html + pub fn verify_result(&self) -> X509VerifyResult { + unsafe { X509VerifyError::from_raw(ffi::X509_STORE_CTX_get_error(self.as_ptr())) } + } + + /// Initializes this context with the given certificate, certificates chain and certificate + /// store. After initializing the context, the `with_context` closure is called with the prepared + /// context. As long as the closure is running, the context stays initialized and can be used + /// to e.g. verify a certificate. The context will be cleaned up, after the closure finished. + /// + /// * `trust` - The certificate store with the trusted certificates. + /// * `cert` - The certificate that should be verified. + /// * `cert_chain` - The certificates chain. + /// * `with_context` - The closure that is called with the initialized context. + /// + /// This corresponds to [`X509_STORE_CTX_init`] before calling `with_context` and to + /// [`X509_STORE_CTX_cleanup`] after calling `with_context`. + /// + /// [`X509_STORE_CTX_init`]: https://www.openssl.org/docs/man1.0.2/crypto/X509_STORE_CTX_init.html + /// [`X509_STORE_CTX_cleanup`]: https://www.openssl.org/docs/man1.0.2/crypto/X509_STORE_CTX_cleanup.html + pub fn init( + &mut self, + trust: &store::X509StoreRef, + cert: &X509Ref, + cert_chain: &StackRef, + with_context: F, + ) -> Result + where + F: FnOnce(&mut X509StoreContextRef) -> Result, + { + struct Cleanup<'a>(&'a mut X509StoreContextRef); + + impl<'a> Drop for Cleanup<'a> { + fn drop(&mut self) { + unsafe { + ffi::X509_STORE_CTX_cleanup(self.0.as_ptr()); + } + } + } + + unsafe { + cvt(ffi::X509_STORE_CTX_init( + self.as_ptr(), + trust.as_ptr(), + cert.as_ptr(), + cert_chain.as_ptr(), + ))?; + + let cleanup = Cleanup(self); + with_context(cleanup.0) + } + } + + /// Verifies the stored certificate. + /// + /// Returns `true` if verification succeeds. The `error` method will return the specific + /// validation error if the certificate was not valid. + /// + /// This will only work inside of a call to `init`. + /// + /// This corresponds to [`X509_verify_cert`]. + /// + /// [`X509_verify_cert`]: https://www.openssl.org/docs/man1.0.2/crypto/X509_verify_cert.html + pub fn verify_cert(&mut self) -> Result { + unsafe { cvt_n(ffi::X509_verify_cert(self.as_ptr())).map(|n| n != 0) } + } + + /// Set the verify result of the context. + /// + /// This corresponds to [`X509_STORE_CTX_set_error`]. + /// + /// [`X509_STORE_CTX_set_error`]: https://www.openssl.org/docs/man1.1.0/crypto/X509_STORE_CTX_set_error.html + pub fn set_error(&mut self, result: X509VerifyResult) { + unsafe { + ffi::X509_STORE_CTX_set_error( + self.as_ptr(), + result + .err() + .as_ref() + .map_or(ffi::X509_V_OK, X509VerifyError::as_raw), + ); + } + } + + /// Returns a reference to the certificate which caused the error or None if + /// no certificate is relevant to the error. + /// + /// This corresponds to [`X509_STORE_CTX_get_current_cert`]. + /// + /// [`X509_STORE_CTX_get_current_cert`]: https://www.openssl.org/docs/man1.1.0/crypto/X509_STORE_CTX_get_current_cert.html + pub fn current_cert(&self) -> Option<&X509Ref> { + unsafe { + let ptr = ffi::X509_STORE_CTX_get_current_cert(self.as_ptr()); + if ptr.is_null() { + None + } else { + Some(X509Ref::from_ptr(ptr)) + } + } + } + + /// Returns a non-negative integer representing the depth in the certificate + /// chain where the error occurred. If it is zero it occurred in the end + /// entity certificate, one if it is the certificate which signed the end + /// entity certificate and so on. + /// + /// This corresponds to [`X509_STORE_CTX_get_error_depth`]. + /// + /// [`X509_STORE_CTX_get_error_depth`]: https://www.openssl.org/docs/man1.1.0/crypto/X509_STORE_CTX_get_error_depth.html + pub fn error_depth(&self) -> u32 { + unsafe { ffi::X509_STORE_CTX_get_error_depth(self.as_ptr()) as u32 } + } + + /// Returns a reference to a complete valid `X509` certificate chain. + /// + /// This corresponds to [`X509_STORE_CTX_get0_chain`]. + /// + /// [`X509_STORE_CTX_get0_chain`]: https://www.openssl.org/docs/man1.1.0/crypto/X509_STORE_CTX_get0_chain.html + pub fn chain(&self) -> Option<&StackRef> { + unsafe { + let chain = X509_STORE_CTX_get0_chain(self.as_ptr()); + + if chain.is_null() { + None + } else { + Some(StackRef::from_ptr(chain)) + } + } + } +} + +/// A builder used to construct an `X509`. +pub struct X509Builder(X509); + +impl X509Builder { + /// Creates a new builder. + pub fn new() -> Result { + unsafe { + ffi::init(); + cvt_p(ffi::X509_new()).map(|p| X509Builder(X509::from_ptr(p))) + } + } + + /// Sets the notAfter constraint on the certificate. + pub fn set_not_after(&mut self, not_after: &Asn1TimeRef) -> Result<(), ErrorStack> { + unsafe { cvt(X509_set1_notAfter(self.0.as_ptr(), not_after.as_ptr())).map(|_| ()) } + } + + /// Sets the notBefore constraint on the certificate. + pub fn set_not_before(&mut self, not_before: &Asn1TimeRef) -> Result<(), ErrorStack> { + unsafe { cvt(X509_set1_notBefore(self.0.as_ptr(), not_before.as_ptr())).map(|_| ()) } + } + + /// Sets the version of the certificate. + /// + /// Note that the version is zero-indexed; that is, a certificate corresponding to version 3 of + /// the X.509 standard should pass `2` to this method. + pub fn set_version(&mut self, version: i32) -> Result<(), ErrorStack> { + unsafe { cvt(ffi::X509_set_version(self.0.as_ptr(), version.into())).map(|_| ()) } + } + + /// Sets the serial number of the certificate. + pub fn set_serial_number(&mut self, serial_number: &Asn1IntegerRef) -> Result<(), ErrorStack> { + unsafe { + cvt(ffi::X509_set_serialNumber( + self.0.as_ptr(), + serial_number.as_ptr(), + )) + .map(|_| ()) + } + } + + /// Sets the issuer name of the certificate. + pub fn set_issuer_name(&mut self, issuer_name: &X509NameRef) -> Result<(), ErrorStack> { + unsafe { + cvt(ffi::X509_set_issuer_name( + self.0.as_ptr(), + issuer_name.as_ptr(), + )) + .map(|_| ()) + } + } + + /// Sets the subject name of the certificate. + /// + /// When building certificates, the `C`, `ST`, and `O` options are common when using the openssl command line tools. + /// The `CN` field is used for the common name, such as a DNS name. + /// + /// ``` + /// use boring::x509::{X509, X509NameBuilder}; + /// + /// let mut x509_name = boring::x509::X509NameBuilder::new().unwrap(); + /// x509_name.append_entry_by_text("C", "US").unwrap(); + /// x509_name.append_entry_by_text("ST", "CA").unwrap(); + /// x509_name.append_entry_by_text("O", "Some organization").unwrap(); + /// x509_name.append_entry_by_text("CN", "www.example.com").unwrap(); + /// let x509_name = x509_name.build(); + /// + /// let mut x509 = boring::x509::X509::builder().unwrap(); + /// x509.set_subject_name(&x509_name).unwrap(); + /// ``` + pub fn set_subject_name(&mut self, subject_name: &X509NameRef) -> Result<(), ErrorStack> { + unsafe { + cvt(ffi::X509_set_subject_name( + self.0.as_ptr(), + subject_name.as_ptr(), + )) + .map(|_| ()) + } + } + + /// Sets the public key associated with the certificate. + pub fn set_pubkey(&mut self, key: &PKeyRef) -> Result<(), ErrorStack> + where + T: HasPublic, + { + unsafe { cvt(ffi::X509_set_pubkey(self.0.as_ptr(), key.as_ptr())).map(|_| ()) } + } + + /// Returns a context object which is needed to create certain X509 extension values. + /// + /// Set `issuer` to `None` if the certificate will be self-signed. + pub fn x509v3_context<'a>( + &'a self, + issuer: Option<&'a X509Ref>, + conf: Option<&'a ConfRef>, + ) -> X509v3Context<'a> { + unsafe { + let mut ctx = mem::zeroed(); + + let issuer = match issuer { + Some(issuer) => issuer.as_ptr(), + None => self.0.as_ptr(), + }; + let subject = self.0.as_ptr(); + ffi::X509V3_set_ctx( + &mut ctx, + issuer, + subject, + ptr::null_mut(), + ptr::null_mut(), + 0, + ); + + // nodb case taken care of since we zeroed ctx above + if let Some(conf) = conf { + ffi::X509V3_set_nconf(&mut ctx, conf.as_ptr()); + } + + X509v3Context(ctx, PhantomData) + } + } + + /// Adds an X509 extension value to the certificate. + /// + /// This works just as `append_extension` except it takes ownership of the `X509Extension`. + pub fn append_extension(&mut self, extension: X509Extension) -> Result<(), ErrorStack> { + self.append_extension2(&extension) + } + + /// Adds an X509 extension value to the certificate. + /// + /// This corresponds to [`X509_add_ext`]. + /// + /// [`X509_add_ext`]: https://www.openssl.org/docs/man1.1.0/man3/X509_get_ext.html + pub fn append_extension2(&mut self, extension: &X509ExtensionRef) -> Result<(), ErrorStack> { + unsafe { + cvt(ffi::X509_add_ext(self.0.as_ptr(), extension.as_ptr(), -1))?; + Ok(()) + } + } + + /// Signs the certificate with a private key. + pub fn sign(&mut self, key: &PKeyRef, hash: MessageDigest) -> Result<(), ErrorStack> + where + T: HasPrivate, + { + unsafe { cvt(ffi::X509_sign(self.0.as_ptr(), key.as_ptr(), hash.as_ptr())).map(|_| ()) } + } + + /// Consumes the builder, returning the certificate. + pub fn build(self) -> X509 { + self.0 + } +} + +foreign_type_and_impl_send_sync! { + type CType = ffi::X509; + fn drop = ffi::X509_free; + + /// An `X509` public key certificate. + pub struct X509; +} + +impl X509Ref { + /// Returns this certificate's subject name. + /// + /// This corresponds to [`X509_get_subject_name`]. + /// + /// [`X509_get_subject_name`]: https://www.openssl.org/docs/man1.1.0/crypto/X509_get_subject_name.html + pub fn subject_name(&self) -> &X509NameRef { + unsafe { + let name = ffi::X509_get_subject_name(self.as_ptr()); + assert!(!name.is_null()); + X509NameRef::from_ptr(name) + } + } + + /// Returns the hash of the certificates subject + /// + /// This corresponds to `X509_subject_name_hash`. + pub fn subject_name_hash(&self) -> u32 { + unsafe { ffi::X509_subject_name_hash(self.as_ptr()) as u32 } + } + + /// Returns this certificate's issuer name. + /// + /// This corresponds to [`X509_get_issuer_name`]. + /// + /// [`X509_get_issuer_name`]: https://www.openssl.org/docs/man1.1.0/crypto/X509_get_subject_name.html + pub fn issuer_name(&self) -> &X509NameRef { + unsafe { + let name = ffi::X509_get_issuer_name(self.as_ptr()); + assert!(!name.is_null()); + X509NameRef::from_ptr(name) + } + } + + /// Returns this certificate's subject alternative name entries, if they exist. + /// + /// This corresponds to [`X509_get_ext_d2i`] called with `NID_subject_alt_name`. + /// + /// [`X509_get_ext_d2i`]: https://www.openssl.org/docs/man1.1.0/crypto/X509_get_ext_d2i.html + pub fn subject_alt_names(&self) -> Option> { + unsafe { + let stack = ffi::X509_get_ext_d2i( + self.as_ptr(), + ffi::NID_subject_alt_name, + ptr::null_mut(), + ptr::null_mut(), + ); + if stack.is_null() { + None + } else { + Some(Stack::from_ptr(stack as *mut _)) + } + } + } + + /// Returns this certificate's issuer alternative name entries, if they exist. + /// + /// This corresponds to [`X509_get_ext_d2i`] called with `NID_issuer_alt_name`. + /// + /// [`X509_get_ext_d2i`]: https://www.openssl.org/docs/man1.1.0/crypto/X509_get_ext_d2i.html + pub fn issuer_alt_names(&self) -> Option> { + unsafe { + let stack = ffi::X509_get_ext_d2i( + self.as_ptr(), + ffi::NID_issuer_alt_name, + ptr::null_mut(), + ptr::null_mut(), + ); + if stack.is_null() { + None + } else { + Some(Stack::from_ptr(stack as *mut _)) + } + } + } + + pub fn public_key(&self) -> Result, ErrorStack> { + unsafe { + let pkey = cvt_p(ffi::X509_get_pubkey(self.as_ptr()))?; + Ok(PKey::from_ptr(pkey)) + } + } + + /// Returns a digest of the DER representation of the certificate. + /// + /// This corresponds to [`X509_digest`]. + /// + /// [`X509_digest`]: https://www.openssl.org/docs/man1.1.0/crypto/X509_digest.html + pub fn digest(&self, hash_type: MessageDigest) -> Result { + unsafe { + let mut digest = DigestBytes { + buf: [0; ffi::EVP_MAX_MD_SIZE as usize], + len: ffi::EVP_MAX_MD_SIZE as usize, + }; + let mut len = ffi::EVP_MAX_MD_SIZE.try_into().unwrap(); + cvt(ffi::X509_digest( + self.as_ptr(), + hash_type.as_ptr(), + digest.buf.as_mut_ptr() as *mut _, + &mut len, + ))?; + digest.len = len as usize; + + Ok(digest) + } + } + + #[deprecated(since = "0.10.9", note = "renamed to digest")] + pub fn fingerprint(&self, hash_type: MessageDigest) -> Result, ErrorStack> { + self.digest(hash_type).map(|b| b.to_vec()) + } + + /// Returns the certificate's Not After validity period. + pub fn not_after(&self) -> &Asn1TimeRef { + unsafe { + let date = X509_getm_notAfter(self.as_ptr()); + assert!(!date.is_null()); + Asn1TimeRef::from_ptr(date) + } + } + + /// Returns the certificate's Not Before validity period. + pub fn not_before(&self) -> &Asn1TimeRef { + unsafe { + let date = X509_getm_notBefore(self.as_ptr()); + assert!(!date.is_null()); + Asn1TimeRef::from_ptr(date) + } + } + + /// Returns the certificate's signature + pub fn signature(&self) -> &Asn1BitStringRef { + unsafe { + let mut signature = ptr::null(); + X509_get0_signature(&mut signature, ptr::null_mut(), self.as_ptr()); + assert!(!signature.is_null()); + Asn1BitStringRef::from_ptr(signature as *mut _) + } + } + + /// Returns the certificate's signature algorithm. + pub fn signature_algorithm(&self) -> &X509AlgorithmRef { + unsafe { + let mut algor = ptr::null(); + X509_get0_signature(ptr::null_mut(), &mut algor, self.as_ptr()); + assert!(!algor.is_null()); + X509AlgorithmRef::from_ptr(algor as *mut _) + } + } + + /// Returns the list of OCSP responder URLs specified in the certificate's Authority Information + /// Access field. + pub fn ocsp_responders(&self) -> Result, ErrorStack> { + unsafe { cvt_p(ffi::X509_get1_ocsp(self.as_ptr())).map(|p| Stack::from_ptr(p)) } + } + + /// Checks that this certificate issued `subject`. + pub fn issued(&self, subject: &X509Ref) -> X509VerifyResult { + unsafe { + let r = ffi::X509_check_issued(self.as_ptr(), subject.as_ptr()); + X509VerifyError::from_raw(r) + } + } + + /// Check if the certificate is signed using the given public key. + /// + /// Only the signature is checked: no other checks (such as certificate chain validity) + /// are performed. + /// + /// Returns `true` if verification succeeds. + /// + /// This corresponds to [`X509_verify"]. + /// + /// [`X509_verify`]: https://www.openssl.org/docs/man1.1.0/crypto/X509_verify.html + pub fn verify(&self, key: &PKeyRef) -> Result + where + T: HasPublic, + { + unsafe { cvt_n(ffi::X509_verify(self.as_ptr(), key.as_ptr())).map(|n| n != 0) } + } + + /// Returns this certificate's serial number. + /// + /// This corresponds to [`X509_get_serialNumber`]. + /// + /// [`X509_get_serialNumber`]: https://www.openssl.org/docs/man1.1.0/crypto/X509_get_serialNumber.html + pub fn serial_number(&self) -> &Asn1IntegerRef { + unsafe { + let r = ffi::X509_get_serialNumber(self.as_ptr()); + assert!(!r.is_null()); + Asn1IntegerRef::from_ptr(r) + } + } + + to_pem! { + /// Serializes the certificate into a PEM-encoded X509 structure. + /// + /// The output will have a header of `-----BEGIN CERTIFICATE-----`. + /// + /// This corresponds to [`PEM_write_bio_X509`]. + /// + /// [`PEM_write_bio_X509`]: https://www.openssl.org/docs/man1.0.2/crypto/PEM_write_bio_X509.html + to_pem, + ffi::PEM_write_bio_X509 + } + + to_der! { + /// Serializes the certificate into a DER-encoded X509 structure. + /// + /// This corresponds to [`i2d_X509`]. + /// + /// [`i2d_X509`]: https://www.openssl.org/docs/man1.1.0/crypto/i2d_X509.html + to_der, + ffi::i2d_X509 + } +} + +impl ToOwned for X509Ref { + type Owned = X509; + + fn to_owned(&self) -> X509 { + unsafe { + X509_up_ref(self.as_ptr()); + X509::from_ptr(self.as_ptr()) + } + } +} + +impl X509 { + /// Returns a new builder. + pub fn builder() -> Result { + X509Builder::new() + } + + from_pem! { + /// Deserializes a PEM-encoded X509 structure. + /// + /// The input should have a header of `-----BEGIN CERTIFICATE-----`. + /// + /// This corresponds to [`PEM_read_bio_X509`]. + /// + /// [`PEM_read_bio_X509`]: https://www.openssl.org/docs/man1.0.2/crypto/PEM_read_bio_X509.html + from_pem, + X509, + ffi::PEM_read_bio_X509 + } + + from_der! { + /// Deserializes a DER-encoded X509 structure. + /// + /// This corresponds to [`d2i_X509`]. + /// + /// [`d2i_X509`]: https://www.openssl.org/docs/manmaster/man3/d2i_X509.html + from_der, + X509, + ffi::d2i_X509, + ::libc::c_long + } + + /// Deserializes a list of PEM-formatted certificates. + pub fn stack_from_pem(pem: &[u8]) -> Result, ErrorStack> { + unsafe { + ffi::init(); + let bio = MemBioSlice::new(pem)?; + + let mut certs = vec![]; + loop { + let r = + ffi::PEM_read_bio_X509(bio.as_ptr(), ptr::null_mut(), None, ptr::null_mut()); + if r.is_null() { + let err = ffi::ERR_peek_last_error(); + + if ffi::ERR_GET_LIB(err) == ffi::ERR_LIB_PEM.0.try_into().unwrap() + && ffi::ERR_GET_REASON(err) == ffi::PEM_R_NO_START_LINE + { + ffi::ERR_clear_error(); + break; + } + + return Err(ErrorStack::get()); + } else { + certs.push(X509::from_ptr(r)); + } + } + + Ok(certs) + } + } +} + +impl Clone for X509 { + fn clone(&self) -> X509 { + X509Ref::to_owned(self) + } +} + +impl fmt::Debug for X509 { + fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + let serial = match &self.serial_number().to_bn() { + Ok(bn) => match bn.to_hex_str() { + Ok(hex) => hex.to_string(), + Err(_) => "".to_string(), + }, + Err(_) => "".to_string(), + }; + let mut debug_struct = formatter.debug_struct("X509"); + debug_struct.field("serial_number", &serial); + debug_struct.field("signature_algorithm", &self.signature_algorithm().object()); + debug_struct.field("issuer", &self.issuer_name()); + debug_struct.field("subject", &self.subject_name()); + if let Some(subject_alt_names) = &self.subject_alt_names() { + debug_struct.field("subject_alt_names", subject_alt_names); + } + debug_struct.field("not_before", &self.not_before()); + debug_struct.field("not_after", &self.not_after()); + + if let Ok(public_key) = &self.public_key() { + debug_struct.field("public_key", public_key); + }; + // TODO: Print extensions once they are supported on the X509 struct. + + debug_struct.finish() + } +} + +impl AsRef for X509Ref { + fn as_ref(&self) -> &X509Ref { + self + } +} + +impl Stackable for X509 { + type StackType = ffi::stack_st_X509; +} + +/// A context object required to construct certain `X509` extension values. +pub struct X509v3Context<'a>(ffi::X509V3_CTX, PhantomData<(&'a X509Ref, &'a ConfRef)>); + +impl<'a> X509v3Context<'a> { + pub fn as_ptr(&self) -> *mut ffi::X509V3_CTX { + &self.0 as *const _ as *mut _ + } +} + +foreign_type_and_impl_send_sync! { + type CType = ffi::X509_EXTENSION; + fn drop = ffi::X509_EXTENSION_free; + + /// Permit additional fields to be added to an `X509` v3 certificate. + pub struct X509Extension; +} + +impl Stackable for X509Extension { + type StackType = ffi::stack_st_X509_EXTENSION; +} + +impl X509Extension { + /// Constructs an X509 extension value. See `man x509v3_config` for information on supported + /// names and their value formats. + /// + /// Some extension types, such as `subjectAlternativeName`, require an `X509v3Context` to be + /// provided. + /// + /// DO NOT CALL THIS WITH UNTRUSTED `value`: `value` is an OpenSSL + /// mini-language that can read arbitrary files. + /// + /// See the extension module for builder types which will construct certain common extensions. + pub fn new( + conf: Option<&ConfRef>, + context: Option<&X509v3Context>, + name: &str, + value: &str, + ) -> Result { + let name = CString::new(name).unwrap(); + let value = CString::new(value).unwrap(); + let mut ctx; + unsafe { + ffi::init(); + let conf = conf.map_or(ptr::null_mut(), ConfRef::as_ptr); + let context_ptr = match context { + Some(c) => c.as_ptr(), + None => { + ctx = mem::zeroed(); + + ffi::X509V3_set_ctx( + &mut ctx, + ptr::null_mut(), + ptr::null_mut(), + ptr::null_mut(), + ptr::null_mut(), + 0, + ); + &mut ctx + } + }; + let name = name.as_ptr() as *mut _; + let value = value.as_ptr() as *mut _; + + cvt_p(ffi::X509V3_EXT_nconf(conf, context_ptr, name, value)) + .map(|p| X509Extension::from_ptr(p)) + } + } + + /// Constructs an X509 extension value. See `man x509v3_config` for information on supported + /// extensions and their value formats. + /// + /// Some extension types, such as `nid::SUBJECT_ALTERNATIVE_NAME`, require an `X509v3Context` to + /// be provided. + /// + /// DO NOT CALL THIS WITH UNTRUSTED `value`: `value` is an OpenSSL + /// mini-language that can read arbitrary files. + /// + /// See the extension module for builder types which will construct certain common extensions. + pub fn new_nid( + conf: Option<&ConfRef>, + context: Option<&X509v3Context>, + name: Nid, + value: &str, + ) -> Result { + let value = CString::new(value).unwrap(); + let mut ctx; + unsafe { + ffi::init(); + let conf = conf.map_or(ptr::null_mut(), ConfRef::as_ptr); + let context_ptr = match context { + Some(c) => c.as_ptr(), + None => { + ctx = mem::zeroed(); + + ffi::X509V3_set_ctx( + &mut ctx, + ptr::null_mut(), + ptr::null_mut(), + ptr::null_mut(), + ptr::null_mut(), + 0, + ); + &mut ctx + } + }; + let name = name.as_raw(); + let value = value.as_ptr() as *mut _; + + cvt_p(ffi::X509V3_EXT_nconf_nid(conf, context_ptr, name, value)) + .map(|p| X509Extension::from_ptr(p)) + } + } + + pub(crate) unsafe fn new_internal( + nid: Nid, + critical: bool, + value: *mut c_void, + ) -> Result { + ffi::init(); + cvt_p(ffi::X509V3_EXT_i2d(nid.as_raw(), critical as _, value)) + .map(|p| X509Extension::from_ptr(p)) + } +} + +impl X509ExtensionRef { + to_der! { + /// Serializes the Extension to its standard DER encoding. + to_der, + ffi::i2d_X509_EXTENSION + } +} + +/// A builder used to construct an `X509Name`. +pub struct X509NameBuilder(X509Name); + +impl X509NameBuilder { + /// Creates a new builder. + pub fn new() -> Result { + unsafe { + ffi::init(); + cvt_p(ffi::X509_NAME_new()).map(|p| X509NameBuilder(X509Name::from_ptr(p))) + } + } + + /// Add a field entry by str. + /// + /// This corresponds to [`X509_NAME_add_entry_by_txt`]. + /// + /// [`X509_NAME_add_entry_by_txt`]: https://www.openssl.org/docs/man1.1.0/crypto/X509_NAME_add_entry_by_txt.html + pub fn append_entry_by_text(&mut self, field: &str, value: &str) -> Result<(), ErrorStack> { + unsafe { + let field = CString::new(field).unwrap(); + assert!(value.len() <= ValueLen::max_value() as usize); + cvt(ffi::X509_NAME_add_entry_by_txt( + self.0.as_ptr(), + field.as_ptr() as *mut _, + ffi::MBSTRING_UTF8, + value.as_ptr(), + value.len() as ValueLen, + -1, + 0, + )) + .map(|_| ()) + } + } + + /// Add a field entry by str with a specific type. + /// + /// This corresponds to [`X509_NAME_add_entry_by_txt`]. + /// + /// [`X509_NAME_add_entry_by_txt`]: https://www.openssl.org/docs/man1.1.0/crypto/X509_NAME_add_entry_by_txt.html + pub fn append_entry_by_text_with_type( + &mut self, + field: &str, + value: &str, + ty: Asn1Type, + ) -> Result<(), ErrorStack> { + unsafe { + let field = CString::new(field).unwrap(); + assert!(value.len() <= ValueLen::max_value() as usize); + cvt(ffi::X509_NAME_add_entry_by_txt( + self.0.as_ptr(), + field.as_ptr() as *mut _, + ty.as_raw(), + value.as_ptr(), + value.len() as ValueLen, + -1, + 0, + )) + .map(|_| ()) + } + } + + /// Add a field entry by NID. + /// + /// This corresponds to [`X509_NAME_add_entry_by_NID`]. + /// + /// [`X509_NAME_add_entry_by_NID`]: https://www.openssl.org/docs/man1.1.0/crypto/X509_NAME_add_entry_by_NID.html + pub fn append_entry_by_nid(&mut self, field: Nid, value: &str) -> Result<(), ErrorStack> { + unsafe { + assert!(value.len() <= ValueLen::max_value() as usize); + cvt(ffi::X509_NAME_add_entry_by_NID( + self.0.as_ptr(), + field.as_raw(), + ffi::MBSTRING_UTF8, + value.as_ptr() as *mut _, + value.len() as ValueLen, + -1, + 0, + )) + .map(|_| ()) + } + } + + /// Add a field entry by NID with a specific type. + /// + /// This corresponds to [`X509_NAME_add_entry_by_NID`]. + /// + /// [`X509_NAME_add_entry_by_NID`]: https://www.openssl.org/docs/man1.1.0/crypto/X509_NAME_add_entry_by_NID.html + pub fn append_entry_by_nid_with_type( + &mut self, + field: Nid, + value: &str, + ty: Asn1Type, + ) -> Result<(), ErrorStack> { + unsafe { + assert!(value.len() <= ValueLen::max_value() as usize); + cvt(ffi::X509_NAME_add_entry_by_NID( + self.0.as_ptr(), + field.as_raw(), + ty.as_raw(), + value.as_ptr() as *mut _, + value.len() as ValueLen, + -1, + 0, + )) + .map(|_| ()) + } + } + + /// Return an `X509Name`. + pub fn build(self) -> X509Name { + // Round-trip through bytes because OpenSSL is not const correct and + // names in a "modified" state compute various things lazily. This can + // lead to data-races because OpenSSL doesn't have locks or anything. + X509Name::from_der(&self.0.to_der().unwrap()).unwrap() + } +} + +#[cfg(not(any(feature = "fips", feature = "fips-link-precompiled")))] +type ValueLen = isize; +#[cfg(any(feature = "fips", feature = "fips-link-precompiled"))] +type ValueLen = i32; + +foreign_type_and_impl_send_sync! { + type CType = ffi::X509_NAME; + fn drop = ffi::X509_NAME_free; + + /// The names of an `X509` certificate. + pub struct X509Name; +} + +impl X509Name { + /// Returns a new builder. + pub fn builder() -> Result { + X509NameBuilder::new() + } + + /// Loads subject names from a file containing PEM-formatted certificates. + /// + /// This is commonly used in conjunction with `SslContextBuilder::set_client_ca_list`. + pub fn load_client_ca_file>(file: P) -> Result, ErrorStack> { + let file = CString::new(file.as_ref().as_os_str().to_str().unwrap()).unwrap(); + unsafe { cvt_p(ffi::SSL_load_client_CA_file(file.as_ptr())).map(|p| Stack::from_ptr(p)) } + } + + from_der! { + /// Deserializes a DER-encoded X509 name structure. + /// + /// This corresponds to [`d2i_X509_NAME`]. + /// + /// [`d2i_X509_NAME`]: https://www.openssl.org/docs/manmaster/man3/d2i_X509_NAME.html + from_der, + X509Name, + ffi::d2i_X509_NAME, + ::libc::c_long + } +} + +impl Stackable for X509Name { + type StackType = ffi::stack_st_X509_NAME; +} + +impl X509NameRef { + /// Returns the name entries by the nid. + pub fn entries_by_nid(&self, nid: Nid) -> X509NameEntries<'_> { + X509NameEntries { + name: self, + nid: Some(nid), + loc: -1, + } + } + + /// Returns an iterator over all `X509NameEntry` values + pub fn entries(&self) -> X509NameEntries<'_> { + X509NameEntries { + name: self, + nid: None, + loc: -1, + } + } + + to_der! { + /// Serializes the certificate into a DER-encoded X509 name structure. + /// + /// This corresponds to [`i2d_X509_NAME`]. + /// + /// [`i2d_X509_NAME`]: https://www.openssl.org/docs/man1.1.0/crypto/i2d_X509_NAME.html + to_der, + ffi::i2d_X509_NAME + } +} + +impl fmt::Debug for X509NameRef { + fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + formatter.debug_list().entries(self.entries()).finish() + } +} + +/// A type to destructure and examine an `X509Name`. +pub struct X509NameEntries<'a> { + name: &'a X509NameRef, + nid: Option, + loc: c_int, +} + +impl<'a> Iterator for X509NameEntries<'a> { + type Item = &'a X509NameEntryRef; + + fn next(&mut self) -> Option<&'a X509NameEntryRef> { + unsafe { + match self.nid { + Some(nid) => { + // There is a `Nid` specified to search for + self.loc = + ffi::X509_NAME_get_index_by_NID(self.name.as_ptr(), nid.as_raw(), self.loc); + if self.loc == -1 { + return None; + } + } + None => { + // Iterate over all `Nid`s + self.loc += 1; + if self.loc >= ffi::X509_NAME_entry_count(self.name.as_ptr()) { + return None; + } + } + } + + let entry = ffi::X509_NAME_get_entry(self.name.as_ptr(), self.loc); + assert!(!entry.is_null()); + + Some(X509NameEntryRef::from_ptr(entry)) + } + } +} + +foreign_type_and_impl_send_sync! { + type CType = ffi::X509_NAME_ENTRY; + fn drop = ffi::X509_NAME_ENTRY_free; + + /// A name entry associated with a `X509Name`. + pub struct X509NameEntry; +} + +impl X509NameEntryRef { + /// Returns the field value of an `X509NameEntry`. + /// + /// This corresponds to [`X509_NAME_ENTRY_get_data`]. + /// + /// [`X509_NAME_ENTRY_get_data`]: https://www.openssl.org/docs/man1.1.0/crypto/X509_NAME_ENTRY_get_data.html + pub fn data(&self) -> &Asn1StringRef { + unsafe { + let data = ffi::X509_NAME_ENTRY_get_data(self.as_ptr()); + Asn1StringRef::from_ptr(data) + } + } + + /// Returns the `Asn1Object` value of an `X509NameEntry`. + /// This is useful for finding out about the actual `Nid` when iterating over all `X509NameEntries`. + /// + /// This corresponds to [`X509_NAME_ENTRY_get_object`]. + /// + /// [`X509_NAME_ENTRY_get_object`]: https://www.openssl.org/docs/man1.1.0/crypto/X509_NAME_ENTRY_get_object.html + pub fn object(&self) -> &Asn1ObjectRef { + unsafe { + let object = ffi::X509_NAME_ENTRY_get_object(self.as_ptr()); + Asn1ObjectRef::from_ptr(object) + } + } +} + +impl fmt::Debug for X509NameEntryRef { + fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + formatter.write_fmt(format_args!("{:?} = {:?}", self.object(), self.data())) + } +} + +/// A builder used to construct an `X509Req`. +pub struct X509ReqBuilder(X509Req); + +impl X509ReqBuilder { + /// Returns a builder for a certificate request. + /// + /// This corresponds to [`X509_REQ_new`]. + /// + ///[`X509_REQ_new`]: https://www.openssl.org/docs/man1.1.0/crypto/X509_REQ_new.html + pub fn new() -> Result { + unsafe { + ffi::init(); + cvt_p(ffi::X509_REQ_new()).map(|p| X509ReqBuilder(X509Req::from_ptr(p))) + } + } + + /// Set the numerical value of the version field. + /// + /// This corresponds to [`X509_REQ_set_version`]. + /// + ///[`X509_REQ_set_version`]: https://www.openssl.org/docs/man1.1.0/crypto/X509_REQ_set_version.html + pub fn set_version(&mut self, version: i32) -> Result<(), ErrorStack> { + unsafe { cvt(ffi::X509_REQ_set_version(self.0.as_ptr(), version.into())).map(|_| ()) } + } + + /// Set the issuer name. + /// + /// This corresponds to [`X509_REQ_set_subject_name`]. + /// + /// [`X509_REQ_set_subject_name`]: https://www.openssl.org/docs/man1.1.0/crypto/X509_REQ_set_subject_name.html + pub fn set_subject_name(&mut self, subject_name: &X509NameRef) -> Result<(), ErrorStack> { + unsafe { + cvt(ffi::X509_REQ_set_subject_name( + self.0.as_ptr(), + subject_name.as_ptr(), + )) + .map(|_| ()) + } + } + + /// Set the public key. + /// + /// This corresponds to [`X509_REQ_set_pubkey`]. + /// + /// [`X509_REQ_set_pubkey`]: https://www.openssl.org/docs/man1.1.0/crypto/X509_REQ_set_pubkey.html + pub fn set_pubkey(&mut self, key: &PKeyRef) -> Result<(), ErrorStack> + where + T: HasPublic, + { + unsafe { cvt(ffi::X509_REQ_set_pubkey(self.0.as_ptr(), key.as_ptr())).map(|_| ()) } + } + + /// Return an `X509v3Context`. This context object can be used to construct + /// certain `X509` extensions. + pub fn x509v3_context<'a>(&'a self, conf: Option<&'a ConfRef>) -> X509v3Context<'a> { + unsafe { + let mut ctx = mem::zeroed(); + + ffi::X509V3_set_ctx( + &mut ctx, + ptr::null_mut(), + ptr::null_mut(), + self.0.as_ptr(), + ptr::null_mut(), + 0, + ); + + // nodb case taken care of since we zeroed ctx above + if let Some(conf) = conf { + ffi::X509V3_set_nconf(&mut ctx, conf.as_ptr()); + } + + X509v3Context(ctx, PhantomData) + } + } + + /// Permits any number of extension fields to be added to the certificate. + pub fn add_extensions( + &mut self, + extensions: &StackRef, + ) -> Result<(), ErrorStack> { + unsafe { + cvt(ffi::X509_REQ_add_extensions( + self.0.as_ptr(), + extensions.as_ptr(), + )) + .map(|_| ()) + } + } + + /// Sign the request using a private key. + /// + /// This corresponds to [`X509_REQ_sign`]. + /// + /// [`X509_REQ_sign`]: https://www.openssl.org/docs/man1.1.0/crypto/X509_REQ_sign.html + pub fn sign(&mut self, key: &PKeyRef, hash: MessageDigest) -> Result<(), ErrorStack> + where + T: HasPrivate, + { + unsafe { + cvt(ffi::X509_REQ_sign( + self.0.as_ptr(), + key.as_ptr(), + hash.as_ptr(), + )) + .map(|_| ()) + } + } + + /// Returns the `X509Req`. + pub fn build(self) -> X509Req { + self.0 + } +} + +foreign_type_and_impl_send_sync! { + type CType = ffi::X509_REQ; + fn drop = ffi::X509_REQ_free; + + /// An `X509` certificate request. + pub struct X509Req; +} + +impl X509Req { + /// A builder for `X509Req`. + pub fn builder() -> Result { + X509ReqBuilder::new() + } + + from_pem! { + /// Deserializes a PEM-encoded PKCS#10 certificate request structure. + /// + /// The input should have a header of `-----BEGIN CERTIFICATE REQUEST-----`. + /// + /// This corresponds to [`PEM_read_bio_X509_REQ`]. + /// + /// [`PEM_read_bio_X509_REQ`]: https://www.openssl.org/docs/man1.0.2/crypto/PEM_read_bio_X509_REQ.html + from_pem, + X509Req, + ffi::PEM_read_bio_X509_REQ + } + + from_der! { + /// Deserializes a DER-encoded PKCS#10 certificate request structure. + /// + /// This corresponds to [`d2i_X509_REQ`]. + /// + /// [`d2i_X509_REQ`]: https://www.openssl.org/docs/man1.1.0/crypto/d2i_X509_REQ.html + from_der, + X509Req, + ffi::d2i_X509_REQ, + ::libc::c_long + } +} + +impl X509ReqRef { + to_pem! { + /// Serializes the certificate request to a PEM-encoded PKCS#10 structure. + /// + /// The output will have a header of `-----BEGIN CERTIFICATE REQUEST-----`. + /// + /// This corresponds to [`PEM_write_bio_X509_REQ`]. + /// + /// [`PEM_write_bio_X509_REQ`]: https://www.openssl.org/docs/man1.0.2/crypto/PEM_write_bio_X509_REQ.html + to_pem, + ffi::PEM_write_bio_X509_REQ + } + + to_der! { + /// Serializes the certificate request to a DER-encoded PKCS#10 structure. + /// + /// This corresponds to [`i2d_X509_REQ`]. + /// + /// [`i2d_X509_REQ`]: https://www.openssl.org/docs/man1.0.2/crypto/i2d_X509_REQ.html + to_der, + ffi::i2d_X509_REQ + } + + /// Returns the numerical value of the version field of the certificate request. + /// + /// This corresponds to [`X509_REQ_get_version`] + /// + /// [`X509_REQ_get_version`]: https://www.openssl.org/docs/man1.1.0/crypto/X509_REQ_get_version.html + pub fn version(&self) -> i32 { + unsafe { X509_REQ_get_version(self.as_ptr()) as i32 } + } + + /// Returns the subject name of the certificate request. + /// + /// This corresponds to [`X509_REQ_get_subject_name`] + /// + /// [`X509_REQ_get_subject_name`]: https://www.openssl.org/docs/man1.1.0/crypto/X509_REQ_get_subject_name.html + pub fn subject_name(&self) -> &X509NameRef { + unsafe { + let name = X509_REQ_get_subject_name(self.as_ptr()); + assert!(!name.is_null()); + X509NameRef::from_ptr(name) + } + } + + /// Returns the public key of the certificate request. + /// + /// This corresponds to [`X509_REQ_get_pubkey"] + /// + /// [`X509_REQ_get_pubkey`]: https://www.openssl.org/docs/man1.1.0/crypto/X509_REQ_get_pubkey.html + pub fn public_key(&self) -> Result, ErrorStack> { + unsafe { + let key = cvt_p(ffi::X509_REQ_get_pubkey(self.as_ptr()))?; + Ok(PKey::from_ptr(key)) + } + } + + /// Check if the certificate request is signed using the given public key. + /// + /// Returns `true` if verification succeeds. + /// + /// This corresponds to [`X509_REQ_verify"]. + /// + /// [`X509_REQ_verify`]: https://www.openssl.org/docs/man1.1.0/crypto/X509_REQ_verify.html + pub fn verify(&self, key: &PKeyRef) -> Result + where + T: HasPublic, + { + unsafe { cvt_n(ffi::X509_REQ_verify(self.as_ptr(), key.as_ptr())).map(|n| n != 0) } + } + + /// Returns the extensions of the certificate request. + /// + /// This corresponds to [`X509_REQ_get_extensions"] + pub fn extensions(&self) -> Result, ErrorStack> { + unsafe { + let extensions = cvt_p(ffi::X509_REQ_get_extensions(self.as_ptr()))?; + Ok(Stack::from_ptr(extensions)) + } + } +} + +/// The result of peer certificate verification. +pub type X509VerifyResult = Result<(), X509VerifyError>; + +#[derive(Copy, Clone, PartialEq, Eq)] +pub struct X509VerifyError(c_int); + +impl fmt::Debug for X509VerifyError { + fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { + fmt.debug_struct("X509VerifyError") + .field("code", &self.0) + .field("error", &self.error_string()) + .finish() + } +} + +impl fmt::Display for X509VerifyError { + fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { + fmt.write_str(self.error_string()) + } +} + +impl Error for X509VerifyError {} + +impl X509VerifyError { + /// Creates an [`X509VerifyResult`] from a raw error number. + /// + /// # Safety + /// + /// Some methods on [`X509VerifyError`] are not thread safe if the error + /// number is invalid. + pub unsafe fn from_raw(err: c_int) -> X509VerifyResult { + if err == ffi::X509_V_OK { + Ok(()) + } else { + Err(X509VerifyError(err)) + } + } + + /// Return the integer representation of an [`X509VerifyError`]. + #[allow(clippy::trivially_copy_pass_by_ref)] + pub fn as_raw(&self) -> c_int { + self.0 + } + + /// Return a human readable error string from the verification error. + /// + /// This corresponds to [`X509_verify_cert_error_string`]. + /// + /// [`X509_verify_cert_error_string`]: https://www.openssl.org/docs/man1.1.0/crypto/X509_verify_cert_error_string.html + #[allow(clippy::trivially_copy_pass_by_ref)] + pub fn error_string(&self) -> &'static str { + ffi::init(); + + unsafe { + let s = ffi::X509_verify_cert_error_string(self.0 as c_long); + str::from_utf8(CStr::from_ptr(s).to_bytes()).unwrap() + } + } +} + +#[allow(missing_docs)] // no need to document the constants +impl X509VerifyError { + pub const UNSPECIFIED: Self = Self(ffi::X509_V_ERR_UNSPECIFIED); + pub const UNABLE_TO_GET_ISSUER_CERT: Self = Self(ffi::X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT); + pub const UNABLE_TO_GET_CRL: Self = Self(ffi::X509_V_ERR_UNABLE_TO_GET_CRL); + pub const UNABLE_TO_DECRYPT_CERT_SIGNATURE: Self = + Self(ffi::X509_V_ERR_UNABLE_TO_DECRYPT_CERT_SIGNATURE); + pub const UNABLE_TO_DECRYPT_CRL_SIGNATURE: Self = + Self(ffi::X509_V_ERR_UNABLE_TO_DECRYPT_CRL_SIGNATURE); + pub const UNABLE_TO_DECODE_ISSUER_PUBLIC_KEY: Self = + Self(ffi::X509_V_ERR_UNABLE_TO_DECODE_ISSUER_PUBLIC_KEY); + pub const CERT_SIGNATURE_FAILURE: Self = Self(ffi::X509_V_ERR_CERT_SIGNATURE_FAILURE); + pub const CRL_SIGNATURE_FAILURE: Self = Self(ffi::X509_V_ERR_CRL_SIGNATURE_FAILURE); + pub const CERT_NOT_YET_VALID: Self = Self(ffi::X509_V_ERR_CERT_NOT_YET_VALID); + pub const CERT_HAS_EXPIRED: Self = Self(ffi::X509_V_ERR_CERT_HAS_EXPIRED); + pub const CRL_NOT_YET_VALID: Self = Self(ffi::X509_V_ERR_CRL_NOT_YET_VALID); + pub const CRL_HAS_EXPIRED: Self = Self(ffi::X509_V_ERR_CRL_HAS_EXPIRED); + pub const ERROR_IN_CERT_NOT_BEFORE_FIELD: Self = + Self(ffi::X509_V_ERR_ERROR_IN_CERT_NOT_BEFORE_FIELD); + pub const ERROR_IN_CERT_NOT_AFTER_FIELD: Self = + Self(ffi::X509_V_ERR_ERROR_IN_CERT_NOT_AFTER_FIELD); + pub const ERROR_IN_CRL_LAST_UPDATE_FIELD: Self = + Self(ffi::X509_V_ERR_ERROR_IN_CRL_LAST_UPDATE_FIELD); + pub const ERROR_IN_CRL_NEXT_UPDATE_FIELD: Self = + Self(ffi::X509_V_ERR_ERROR_IN_CRL_NEXT_UPDATE_FIELD); + pub const OUT_OF_MEM: Self = Self(ffi::X509_V_ERR_OUT_OF_MEM); + pub const DEPTH_ZERO_SELF_SIGNED_CERT: Self = Self(ffi::X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT); + pub const SELF_SIGNED_CERT_IN_CHAIN: Self = Self(ffi::X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN); + pub const UNABLE_TO_GET_ISSUER_CERT_LOCALLY: Self = + Self(ffi::X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY); + pub const UNABLE_TO_VERIFY_LEAF_SIGNATURE: Self = + Self(ffi::X509_V_ERR_UNABLE_TO_VERIFY_LEAF_SIGNATURE); + pub const CERT_CHAIN_TOO_LONG: Self = Self(ffi::X509_V_ERR_CERT_CHAIN_TOO_LONG); + pub const CERT_REVOKED: Self = Self(ffi::X509_V_ERR_CERT_REVOKED); + pub const INVALID_CA: Self = Self(ffi::X509_V_ERR_INVALID_CA); + pub const PATH_LENGTH_EXCEEDED: Self = Self(ffi::X509_V_ERR_PATH_LENGTH_EXCEEDED); + pub const INVALID_PURPOSE: Self = Self(ffi::X509_V_ERR_INVALID_PURPOSE); + pub const CERT_UNTRUSTED: Self = Self(ffi::X509_V_ERR_CERT_UNTRUSTED); + pub const CERT_REJECTED: Self = Self(ffi::X509_V_ERR_CERT_REJECTED); + pub const SUBJECT_ISSUER_MISMATCH: Self = Self(ffi::X509_V_ERR_SUBJECT_ISSUER_MISMATCH); + pub const AKID_SKID_MISMATCH: Self = Self(ffi::X509_V_ERR_AKID_SKID_MISMATCH); + pub const AKID_ISSUER_SERIAL_MISMATCH: Self = Self(ffi::X509_V_ERR_AKID_ISSUER_SERIAL_MISMATCH); + pub const KEYUSAGE_NO_CERTSIGN: Self = Self(ffi::X509_V_ERR_KEYUSAGE_NO_CERTSIGN); + pub const UNABLE_TO_GET_CRL_ISSUER: Self = Self(ffi::X509_V_ERR_UNABLE_TO_GET_CRL_ISSUER); + pub const UNHANDLED_CRITICAL_EXTENSION: Self = + Self(ffi::X509_V_ERR_UNHANDLED_CRITICAL_EXTENSION); + pub const KEYUSAGE_NO_CRL_SIGN: Self = Self(ffi::X509_V_ERR_KEYUSAGE_NO_CRL_SIGN); + pub const UNHANDLED_CRITICAL_CRL_EXTENSION: Self = + Self(ffi::X509_V_ERR_UNHANDLED_CRITICAL_CRL_EXTENSION); + pub const INVALID_NON_CA: Self = Self(ffi::X509_V_ERR_INVALID_NON_CA); + pub const PROXY_PATH_LENGTH_EXCEEDED: Self = Self(ffi::X509_V_ERR_PROXY_PATH_LENGTH_EXCEEDED); + pub const KEYUSAGE_NO_DIGITAL_SIGNATURE: Self = + Self(ffi::X509_V_ERR_KEYUSAGE_NO_DIGITAL_SIGNATURE); + pub const PROXY_CERTIFICATES_NOT_ALLOWED: Self = + Self(ffi::X509_V_ERR_PROXY_CERTIFICATES_NOT_ALLOWED); + pub const INVALID_EXTENSION: Self = Self(ffi::X509_V_ERR_INVALID_EXTENSION); + pub const INVALID_POLICY_EXTENSION: Self = Self(ffi::X509_V_ERR_INVALID_POLICY_EXTENSION); + pub const NO_EXPLICIT_POLICY: Self = Self(ffi::X509_V_ERR_NO_EXPLICIT_POLICY); + pub const DIFFERENT_CRL_SCOPE: Self = Self(ffi::X509_V_ERR_DIFFERENT_CRL_SCOPE); + pub const UNSUPPORTED_EXTENSION_FEATURE: Self = + Self(ffi::X509_V_ERR_UNSUPPORTED_EXTENSION_FEATURE); + pub const UNNESTED_RESOURCE: Self = Self(ffi::X509_V_ERR_UNNESTED_RESOURCE); + pub const PERMITTED_VIOLATION: Self = Self(ffi::X509_V_ERR_PERMITTED_VIOLATION); + pub const EXCLUDED_VIOLATION: Self = Self(ffi::X509_V_ERR_EXCLUDED_VIOLATION); + pub const SUBTREE_MINMAX: Self = Self(ffi::X509_V_ERR_SUBTREE_MINMAX); + pub const APPLICATION_VERIFICATION: Self = Self(ffi::X509_V_ERR_APPLICATION_VERIFICATION); + pub const UNSUPPORTED_CONSTRAINT_TYPE: Self = Self(ffi::X509_V_ERR_UNSUPPORTED_CONSTRAINT_TYPE); + pub const UNSUPPORTED_CONSTRAINT_SYNTAX: Self = + Self(ffi::X509_V_ERR_UNSUPPORTED_CONSTRAINT_SYNTAX); + pub const UNSUPPORTED_NAME_SYNTAX: Self = Self(ffi::X509_V_ERR_UNSUPPORTED_NAME_SYNTAX); + pub const CRL_PATH_VALIDATION_ERROR: Self = Self(ffi::X509_V_ERR_CRL_PATH_VALIDATION_ERROR); + pub const HOSTNAME_MISMATCH: Self = Self(ffi::X509_V_ERR_HOSTNAME_MISMATCH); + pub const EMAIL_MISMATCH: Self = Self(ffi::X509_V_ERR_EMAIL_MISMATCH); + pub const IP_ADDRESS_MISMATCH: Self = Self(ffi::X509_V_ERR_IP_ADDRESS_MISMATCH); + pub const INVALID_CALL: Self = Self(ffi::X509_V_ERR_INVALID_CALL); + pub const STORE_LOOKUP: Self = Self(ffi::X509_V_ERR_STORE_LOOKUP); + pub const NAME_CONSTRAINTS_WITHOUT_SANS: Self = + Self(ffi::X509_V_ERR_NAME_CONSTRAINTS_WITHOUT_SANS); +} + +foreign_type_and_impl_send_sync! { + type CType = ffi::GENERAL_NAME; + fn drop = ffi::GENERAL_NAME_free; + + /// An `X509` certificate alternative names. + pub struct GeneralName; +} + +impl GeneralName { + unsafe fn new( + type_: c_int, + asn1_type: Asn1Type, + value: &[u8], + ) -> Result { + ffi::init(); + let gn = GeneralName::from_ptr(cvt_p(ffi::GENERAL_NAME_new())?); + (*gn.as_ptr()).type_ = type_; + let s = cvt_p(ffi::ASN1_STRING_type_new(asn1_type.as_raw()))?; + ffi::ASN1_STRING_set(s, value.as_ptr().cast(), value.len().try_into().unwrap()); + + (*gn.as_ptr()).d.ptr = s.cast(); + + Ok(gn) + } + + pub(crate) fn new_email(email: &[u8]) -> Result { + unsafe { GeneralName::new(ffi::GEN_EMAIL, Asn1Type::IA5STRING, email) } + } + + pub(crate) fn new_dns(dns: &[u8]) -> Result { + unsafe { GeneralName::new(ffi::GEN_DNS, Asn1Type::IA5STRING, dns) } + } + + pub(crate) fn new_uri(uri: &[u8]) -> Result { + unsafe { GeneralName::new(ffi::GEN_URI, Asn1Type::IA5STRING, uri) } + } + + pub(crate) fn new_ip(ip: IpAddr) -> Result { + match ip { + IpAddr::V4(addr) => unsafe { + GeneralName::new(ffi::GEN_IPADD, Asn1Type::OCTET_STRING, &addr.octets()) + }, + IpAddr::V6(addr) => unsafe { + GeneralName::new(ffi::GEN_IPADD, Asn1Type::OCTET_STRING, &addr.octets()) + }, + } + } + + pub(crate) fn new_rid(oid: Asn1Object) -> Result { + unsafe { + ffi::init(); + let gn = cvt_p(ffi::GENERAL_NAME_new())?; + (*gn).type_ = ffi::GEN_RID; + (*gn).d.registeredID = oid.as_ptr(); + + mem::forget(oid); + + Ok(GeneralName::from_ptr(gn)) + } + } +} + +impl GeneralNameRef { + fn ia5_string(&self, ffi_type: c_int) -> Option<&str> { + unsafe { + if (*self.as_ptr()).type_ != ffi_type { + return None; + } + + let ptr = ASN1_STRING_get0_data((*self.as_ptr()).d.ia5 as *mut _); + let len = ffi::ASN1_STRING_length((*self.as_ptr()).d.ia5 as *mut _); + + let slice = slice::from_raw_parts(ptr, len as usize); + // IA5Strings are stated to be ASCII (specifically IA5). Hopefully + // OpenSSL checks that when loading a certificate but if not we'll + // use this instead of from_utf8_unchecked just in case. + str::from_utf8(slice).ok() + } + } + + /// Returns the contents of this `GeneralName` if it is an `rfc822Name`. + pub fn email(&self) -> Option<&str> { + self.ia5_string(ffi::GEN_EMAIL) + } + + /// Returns the contents of this `GeneralName` if it is a `dNSName`. + pub fn dnsname(&self) -> Option<&str> { + self.ia5_string(ffi::GEN_DNS) + } + + /// Returns the contents of this `GeneralName` if it is an `uniformResourceIdentifier`. + pub fn uri(&self) -> Option<&str> { + self.ia5_string(ffi::GEN_URI) + } + + /// Returns the contents of this `GeneralName` if it is an `iPAddress`. + pub fn ipaddress(&self) -> Option<&[u8]> { + unsafe { + if (*self.as_ptr()).type_ != ffi::GEN_IPADD { + return None; + } + + let ptr = ASN1_STRING_get0_data((*self.as_ptr()).d.ip as *mut _); + let len = ffi::ASN1_STRING_length((*self.as_ptr()).d.ip as *mut _); + + Some(slice::from_raw_parts(ptr, len as usize)) + } + } +} + +impl fmt::Debug for GeneralNameRef { + fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + if let Some(email) = self.email() { + formatter.write_str(email) + } else if let Some(dnsname) = self.dnsname() { + formatter.write_str(dnsname) + } else if let Some(uri) = self.uri() { + formatter.write_str(uri) + } else if let Some(ipaddress) = self.ipaddress() { + let result = String::from_utf8_lossy(ipaddress); + formatter.write_str(&result) + } else { + formatter.write_str("(empty)") + } + } +} + +impl Stackable for GeneralName { + type StackType = ffi::stack_st_GENERAL_NAME; +} + +foreign_type_and_impl_send_sync! { + type CType = ffi::X509_ALGOR; + fn drop = ffi::X509_ALGOR_free; + + /// An `X509` certificate signature algorithm. + pub struct X509Algorithm; +} + +impl X509AlgorithmRef { + /// Returns the ASN.1 OID of this algorithm. + pub fn object(&self) -> &Asn1ObjectRef { + unsafe { + let mut oid = ptr::null(); + X509_ALGOR_get0(&mut oid, ptr::null_mut(), ptr::null_mut(), self.as_ptr()); + assert!(!oid.is_null()); + Asn1ObjectRef::from_ptr(oid as *mut _) + } + } +} + +foreign_type_and_impl_send_sync! { + type CType = ffi::X509_OBJECT; + fn drop = X509_OBJECT_free; + + /// An `X509` or an X509 certificate revocation list. + pub struct X509Object; +} + +impl X509ObjectRef { + pub fn x509(&self) -> Option<&X509Ref> { + unsafe { + let ptr = X509_OBJECT_get0_X509(self.as_ptr()); + if ptr.is_null() { + None + } else { + Some(X509Ref::from_ptr(ptr)) + } + } + } +} + +impl Stackable for X509Object { + type StackType = ffi::stack_st_X509_OBJECT; +} + +use crate::ffi::{X509_get0_signature, X509_getm_notAfter, X509_getm_notBefore, X509_up_ref}; + +use crate::ffi::{ + ASN1_STRING_get0_data, X509_ALGOR_get0, X509_REQ_get_subject_name, X509_REQ_get_version, + X509_STORE_CTX_get0_chain, X509_set1_notAfter, X509_set1_notBefore, +}; + +use crate::ffi::X509_OBJECT_get0_X509; + +#[allow(bad_style)] +unsafe fn X509_OBJECT_free(x: *mut ffi::X509_OBJECT) { + ffi::X509_OBJECT_free_contents(x); + ffi::OPENSSL_free(x as *mut libc::c_void); +} diff --git a/crates/boring/src/x509/store.rs b/crates/boring/src/x509/store.rs new file mode 100644 index 000000000..7033450a3 --- /dev/null +++ b/crates/boring/src/x509/store.rs @@ -0,0 +1,112 @@ +//! Describe a context in which to verify an `X509` certificate. +//! +//! The `X509` certificate store holds trusted CA certificates used to verify +//! peer certificates. +//! +//! # Example +//! +//! ```rust +//! use boring::x509::store::{X509StoreBuilder, X509Store}; +//! use boring::x509::{X509, X509Name}; +//! use boring::asn1::Asn1Time; +//! use boring::pkey::PKey; +//! use boring::hash::MessageDigest; +//! use boring::rsa::Rsa; +//! use boring::nid::Nid; +//! +//! let rsa = Rsa::generate(2048).unwrap(); +//! let pkey = PKey::from_rsa(rsa).unwrap(); +//! let mut name = X509Name::builder().unwrap(); +//! +//! name.append_entry_by_nid(Nid::COMMONNAME, "foobar.com").unwrap(); +//! +//! let name = name.build(); +//! let mut builder = X509::builder().unwrap(); +//! +//! // Sep 27th, 2016 +//! let sample_time = Asn1Time::from_unix(1474934400).unwrap(); +//! +//! builder.set_version(2).unwrap(); +//! builder.set_subject_name(&name).unwrap(); +//! builder.set_issuer_name(&name).unwrap(); +//! builder.set_pubkey(&pkey).unwrap(); +//! builder.set_not_before(&sample_time); +//! builder.set_not_after(&sample_time); +//! builder.sign(&pkey, MessageDigest::sha256()).unwrap(); +//! +//! let certificate: X509 = builder.build(); +//! let mut builder = X509StoreBuilder::new().unwrap(); +//! let _ = builder.add_cert(certificate); +//! let store: X509Store = builder.build(); +//! ``` + +use crate::ffi; +use foreign_types::{ForeignType, ForeignTypeRef}; +use std::mem; + +use crate::error::ErrorStack; +use crate::stack::StackRef; +use crate::x509::{X509Object, X509}; +use crate::{cvt, cvt_p}; + +foreign_type_and_impl_send_sync! { + type CType = ffi::X509_STORE; + fn drop = ffi::X509_STORE_free; + + /// A builder type used to construct an `X509Store`. + pub struct X509StoreBuilder; +} + +impl X509StoreBuilder { + /// Returns a builder for a certificate store. + /// + /// The store is initially empty. + pub fn new() -> Result { + unsafe { + ffi::init(); + + cvt_p(ffi::X509_STORE_new()).map(|p| X509StoreBuilder::from_ptr(p)) + } + } + + /// Constructs the `X509Store`. + pub fn build(self) -> X509Store { + let store = X509Store(self.0); + mem::forget(self); + store + } +} + +impl X509StoreBuilderRef { + /// Adds a certificate to the certificate store. + // FIXME should take an &X509Ref + pub fn add_cert(&mut self, cert: X509) -> Result<(), ErrorStack> { + unsafe { cvt(ffi::X509_STORE_add_cert(self.as_ptr(), cert.as_ptr())).map(|_| ()) } + } + + /// Load certificates from their default locations. + /// + /// These locations are read from the `SSL_CERT_FILE` and `SSL_CERT_DIR` + /// environment variables if present, or defaults specified at OpenSSL + /// build time otherwise. + pub fn set_default_paths(&mut self) -> Result<(), ErrorStack> { + unsafe { cvt(ffi::X509_STORE_set_default_paths(self.as_ptr())).map(|_| ()) } + } +} + +foreign_type_and_impl_send_sync! { + type CType = ffi::X509_STORE; + fn drop = ffi::X509_STORE_free; + + /// A certificate store to hold trusted `X509` certificates. + pub struct X509Store; +} + +impl X509StoreRef { + /// Get a reference to the cache of certificates in this store. + pub fn objects(&self) -> &StackRef { + unsafe { StackRef::from_ptr(X509_STORE_get0_objects(self.as_ptr())) } + } +} + +use crate::ffi::X509_STORE_get0_objects; diff --git a/crates/boring/src/x509/tests.rs b/crates/boring/src/x509/tests.rs new file mode 100644 index 000000000..e656873d3 --- /dev/null +++ b/crates/boring/src/x509/tests.rs @@ -0,0 +1,458 @@ +use hex::{self, FromHex}; + +use crate::asn1::Asn1Time; +use crate::bn::{BigNum, MsbOption}; +use crate::hash::MessageDigest; +use crate::nid::Nid; +use crate::pkey::{PKey, Private}; +use crate::rsa::Rsa; +use crate::stack::Stack; +use crate::x509::extension::{ + AuthorityKeyIdentifier, BasicConstraints, ExtendedKeyUsage, KeyUsage, SubjectAlternativeName, + SubjectKeyIdentifier, +}; +use crate::x509::store::X509StoreBuilder; +use crate::x509::{X509Extension, X509Name, X509Req, X509StoreContext, X509}; + +fn pkey() -> PKey { + let rsa = Rsa::generate(2048).unwrap(); + PKey::from_rsa(rsa).unwrap() +} + +#[test] +fn test_cert_loading() { + let cert = include_bytes!("../../test/cert.pem"); + let cert = X509::from_pem(cert).unwrap(); + let fingerprint = cert.digest(MessageDigest::sha1()).unwrap(); + + let hash_str = "59172d9313e84459bcff27f967e79e6e9217e584"; + let hash_vec = Vec::from_hex(hash_str).unwrap(); + + assert_eq!(hash_vec, &*fingerprint); +} + +#[test] +fn test_debug() { + let cert = include_bytes!("../../test/cert.pem"); + let cert = X509::from_pem(cert).unwrap(); + let debugged = format!("{:#?}", cert); + + assert!(debugged.contains(r#"serial_number: "8771f7bdee982fa5""#)); + assert!(debugged.contains(r#"signature_algorithm: sha256WithRSAEncryption"#)); + assert!(debugged.contains(r#"countryName = "AU""#)); + assert!(debugged.contains(r#"stateOrProvinceName = "Some-State""#)); + assert!(debugged.contains(r#"not_before: Aug 14 17:00:03 2016 GMT"#)); + assert!(debugged.contains(r#"not_after: Aug 12 17:00:03 2026 GMT"#)); +} + +#[test] +fn test_cert_issue_validity() { + let cert = include_bytes!("../../test/cert.pem"); + let cert = X509::from_pem(cert).unwrap(); + let not_before = cert.not_before().to_string(); + let not_after = cert.not_after().to_string(); + + assert_eq!(not_before, "Aug 14 17:00:03 2016 GMT"); + assert_eq!(not_after, "Aug 12 17:00:03 2026 GMT"); +} + +#[test] +fn test_save_der() { + let cert = include_bytes!("../../test/cert.pem"); + let cert = X509::from_pem(cert).unwrap(); + + let der = cert.to_der().unwrap(); + assert!(!der.is_empty()); +} + +#[test] +fn test_subject_read_cn() { + let cert = include_bytes!("../../test/cert.pem"); + let cert = X509::from_pem(cert).unwrap(); + let subject = cert.subject_name(); + let cn = subject.entries_by_nid(Nid::COMMONNAME).next().unwrap(); + assert_eq!(cn.data().as_slice(), b"foobar.com") +} + +#[test] +fn test_nid_values() { + let cert = include_bytes!("../../test/nid_test_cert.pem"); + let cert = X509::from_pem(cert).unwrap(); + let subject = cert.subject_name(); + + let cn = subject.entries_by_nid(Nid::COMMONNAME).next().unwrap(); + assert_eq!(cn.data().as_slice(), b"example.com"); + + let email = subject + .entries_by_nid(Nid::PKCS9_EMAILADDRESS) + .next() + .unwrap(); + assert_eq!(email.data().as_slice(), b"test@example.com"); + + let friendly = subject.entries_by_nid(Nid::FRIENDLYNAME).next().unwrap(); + assert_eq!(&**friendly.data().as_utf8().unwrap(), "Example"); +} + +#[test] +fn test_nameref_iterator() { + let cert = include_bytes!("../../test/nid_test_cert.pem"); + let cert = X509::from_pem(cert).unwrap(); + let subject = cert.subject_name(); + let mut all_entries = subject.entries(); + + let email = all_entries.next().unwrap(); + assert_eq!( + email.object().nid().as_raw(), + Nid::PKCS9_EMAILADDRESS.as_raw() + ); + assert_eq!(email.data().as_slice(), b"test@example.com"); + + let cn = all_entries.next().unwrap(); + assert_eq!(cn.object().nid().as_raw(), Nid::COMMONNAME.as_raw()); + assert_eq!(cn.data().as_slice(), b"example.com"); + + let friendly = all_entries.next().unwrap(); + assert_eq!(friendly.object().nid().as_raw(), Nid::FRIENDLYNAME.as_raw()); + assert_eq!(&**friendly.data().as_utf8().unwrap(), "Example"); + + if all_entries.next().is_some() { + panic!(); + } +} + +#[test] +fn test_nid_uid_value() { + let cert = include_bytes!("../../test/nid_uid_test_cert.pem"); + let cert = X509::from_pem(cert).unwrap(); + let subject = cert.subject_name(); + + let cn = subject.entries_by_nid(Nid::USERID).next().unwrap(); + assert_eq!(cn.data().as_slice(), b"this is the userId"); +} + +#[test] +fn test_subject_alt_name() { + let cert = include_bytes!("../../test/alt_name_cert.pem"); + let cert = X509::from_pem(cert).unwrap(); + + let subject_alt_names = cert.subject_alt_names().unwrap(); + assert_eq!(5, subject_alt_names.len()); + assert_eq!(Some("example.com"), subject_alt_names[0].dnsname()); + assert_eq!(subject_alt_names[1].ipaddress(), Some(&[127, 0, 0, 1][..])); + assert_eq!( + subject_alt_names[2].ipaddress(), + Some(&b"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x01"[..]) + ); + assert_eq!(Some("test@example.com"), subject_alt_names[3].email()); + assert_eq!(Some("http://www.example.com"), subject_alt_names[4].uri()); +} + +#[test] +fn test_subject_alt_name_iter() { + let cert = include_bytes!("../../test/alt_name_cert.pem"); + let cert = X509::from_pem(cert).unwrap(); + + let subject_alt_names = cert.subject_alt_names().unwrap(); + let mut subject_alt_names_iter = subject_alt_names.iter(); + assert_eq!( + subject_alt_names_iter.next().unwrap().dnsname(), + Some("example.com") + ); + assert_eq!( + subject_alt_names_iter.next().unwrap().ipaddress(), + Some(&[127, 0, 0, 1][..]) + ); + assert_eq!( + subject_alt_names_iter.next().unwrap().ipaddress(), + Some(&b"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x01"[..]) + ); + assert_eq!( + subject_alt_names_iter.next().unwrap().email(), + Some("test@example.com") + ); + assert_eq!( + subject_alt_names_iter.next().unwrap().uri(), + Some("http://www.example.com") + ); + assert!(subject_alt_names_iter.next().is_none()); +} + +#[test] +fn x509_builder() { + let pkey = pkey(); + + let mut name = X509Name::builder().unwrap(); + name.append_entry_by_nid(Nid::COMMONNAME, "foobar.com") + .unwrap(); + let name = name.build(); + + let mut builder = X509::builder().unwrap(); + builder.set_version(2).unwrap(); + builder.set_subject_name(&name).unwrap(); + builder.set_issuer_name(&name).unwrap(); + builder + .set_not_before(&Asn1Time::days_from_now(0).unwrap()) + .unwrap(); + builder + .set_not_after(&Asn1Time::days_from_now(365).unwrap()) + .unwrap(); + builder.set_pubkey(&pkey).unwrap(); + + let mut serial = BigNum::new().unwrap(); + serial.rand(128, MsbOption::MAYBE_ZERO, false).unwrap(); + builder + .set_serial_number(&serial.to_asn1_integer().unwrap()) + .unwrap(); + + let basic_constraints = BasicConstraints::new().critical().ca().build().unwrap(); + builder.append_extension(basic_constraints).unwrap(); + let key_usage = KeyUsage::new() + .digital_signature() + .key_encipherment() + .build() + .unwrap(); + builder.append_extension(key_usage).unwrap(); + let ext_key_usage = ExtendedKeyUsage::new() + .client_auth() + .server_auth() + .other("2.999.1") + .build() + .unwrap(); + builder.append_extension(ext_key_usage).unwrap(); + let subject_key_identifier = SubjectKeyIdentifier::new() + .build(&builder.x509v3_context(None, None)) + .unwrap(); + builder.append_extension(subject_key_identifier).unwrap(); + let authority_key_identifier = AuthorityKeyIdentifier::new() + .keyid(true) + .build(&builder.x509v3_context(None, None)) + .unwrap(); + builder.append_extension(authority_key_identifier).unwrap(); + let subject_alternative_name = SubjectAlternativeName::new() + .dns("example.com") + .build(&builder.x509v3_context(None, None)) + .unwrap(); + builder.append_extension(subject_alternative_name).unwrap(); + + builder.sign(&pkey, MessageDigest::sha256()).unwrap(); + + let x509 = builder.build(); + + assert!(pkey.public_eq(&x509.public_key().unwrap())); + assert!(x509.verify(&pkey).unwrap()); + + let cn = x509 + .subject_name() + .entries_by_nid(Nid::COMMONNAME) + .next() + .unwrap(); + assert_eq!(cn.data().as_slice(), b"foobar.com"); + assert_eq!(serial, x509.serial_number().to_bn().unwrap()); +} + +#[test] +fn x509_extension_new() { + assert!(X509Extension::new(None, None, "crlDistributionPoints", "section").is_err()); + assert!(X509Extension::new(None, None, "proxyCertInfo", "").is_err()); + assert!(X509Extension::new(None, None, "certificatePolicies", "").is_err()); + assert!(X509Extension::new(None, None, "subjectAltName", "dirName:section").is_err()); +} + +#[test] +fn x509_extension_to_der() { + let builder = X509::builder().unwrap(); + + for (ext, expected) in [ + ( + BasicConstraints::new().critical().ca().build().unwrap(), + b"0\x0f\x06\x03U\x1d\x13\x01\x01\xff\x04\x050\x03\x01\x01\xff" as &[u8], + ), + ( + SubjectAlternativeName::new() + .dns("example.com,DNS:example2.com") + .build(&builder.x509v3_context(None, None)) + .unwrap(), + b"0'\x06\x03U\x1d\x11\x04 0\x1e\x82\x1cexample.com,DNS:example2.com", + ), + ( + SubjectAlternativeName::new() + .rid("1.2.3.4") + .uri("https://example.com") + .build(&builder.x509v3_context(None, None)) + .unwrap(), + b"0#\x06\x03U\x1d\x11\x04\x1c0\x1a\x88\x03*\x03\x04\x86\x13https://example.com", + ), + ( + ExtendedKeyUsage::new() + .server_auth() + .other("2.999.1") + .other("clientAuth") + .build() + .unwrap(), + b"0\x22\x06\x03U\x1d%\x04\x1b0\x19\x06\x08+\x06\x01\x05\x05\x07\x03\x01\x06\x03\x887\x01\x06\x08+\x06\x01\x05\x05\x07\x03\x02", + ), + ] { + assert_eq!(&ext.to_der().unwrap(), expected); + } +} + +#[test] +fn eku_invalid_other() { + assert!(ExtendedKeyUsage::new() + .other("1.1.1.1.1,2.2.2.2.2") + .build() + .is_err()); +} + +#[test] +fn x509_req_builder() { + let pkey = pkey(); + + let mut name = X509Name::builder().unwrap(); + name.append_entry_by_nid(Nid::COMMONNAME, "foobar.com") + .unwrap(); + let name = name.build(); + + let mut builder = X509Req::builder().unwrap(); + builder.set_version(0).unwrap(); + builder.set_subject_name(&name).unwrap(); + builder.set_pubkey(&pkey).unwrap(); + + let mut extensions = Stack::new().unwrap(); + let key_usage = KeyUsage::new() + .digital_signature() + .key_encipherment() + .build() + .unwrap(); + extensions.push(key_usage).unwrap(); + let subject_alternative_name = SubjectAlternativeName::new() + .dns("example.com") + .build(&builder.x509v3_context(None)) + .unwrap(); + extensions.push(subject_alternative_name).unwrap(); + builder.add_extensions(&extensions).unwrap(); + + builder.sign(&pkey, MessageDigest::sha256()).unwrap(); + + let req = builder.build(); + assert!(req.public_key().unwrap().public_eq(&pkey)); + assert_eq!(req.extensions().unwrap().len(), extensions.len()); + assert!(req.verify(&pkey).unwrap()); +} + +#[test] +fn test_stack_from_pem() { + let certs = include_bytes!("../../test/certs.pem"); + let certs = X509::stack_from_pem(certs).unwrap(); + + assert_eq!(certs.len(), 2); + assert_eq!( + hex::encode(certs[0].digest(MessageDigest::sha1()).unwrap()), + "59172d9313e84459bcff27f967e79e6e9217e584" + ); + assert_eq!( + hex::encode(certs[1].digest(MessageDigest::sha1()).unwrap()), + "c0cbdf7cdd03c9773e5468e1f6d2da7d5cbb1875" + ); +} + +#[test] +fn issued() { + let cert = include_bytes!("../../test/cert.pem"); + let cert = X509::from_pem(cert).unwrap(); + let ca = include_bytes!("../../test/root-ca.pem"); + let ca = X509::from_pem(ca).unwrap(); + + assert_eq!(ca.issued(&cert), Ok(())); + assert!(cert.issued(&cert).is_err()); +} + +#[test] +fn signature() { + let cert = include_bytes!("../../test/cert.pem"); + let cert = X509::from_pem(cert).unwrap(); + let signature = cert.signature(); + assert_eq!( + hex::encode(signature.as_slice()), + "4af607b889790b43470442cfa551cdb8b6d0b0340d2958f76b9e3ef6ad4992230cead6842587f0ecad5\ + 78e6e11a221521e940187e3d6652de14e84e82f6671f097cc47932e022add3c0cb54a26bf27fa84c107\ + 4971caa6bee2e42d34a5b066c427f2d452038082b8073993399548088429de034fdd589dcfb0dd33be7\ + ebdfdf698a28d628a89568881d658151276bde333600969502c4e62e1d3470a683364dfb241f78d310a\ + 89c119297df093eb36b7fd7540224f488806780305d1e79ffc938fe2275441726522ab36d88348e6c51\ + f13dcc46b5e1cdac23c974fd5ef86aa41e91c9311655090a52333bc79687c748d833595d4c5f987508f\ + e121997410d37c" + ); + let algorithm = cert.signature_algorithm(); + assert_eq!(algorithm.object().nid(), Nid::SHA256WITHRSAENCRYPTION); + assert_eq!(algorithm.object().to_string(), "sha256WithRSAEncryption"); +} + +#[test] +#[allow(clippy::redundant_clone)] +fn clone_x509() { + let cert = include_bytes!("../../test/cert.pem"); + let cert = X509::from_pem(cert).unwrap(); + drop(cert.clone()); +} + +#[test] +fn test_verify_cert() { + let cert = include_bytes!("../../test/cert.pem"); + let cert = X509::from_pem(cert).unwrap(); + let ca = include_bytes!("../../test/root-ca.pem"); + let ca = X509::from_pem(ca).unwrap(); + let chain = Stack::new().unwrap(); + + let mut store_bldr = X509StoreBuilder::new().unwrap(); + store_bldr.add_cert(ca).unwrap(); + let store = store_bldr.build(); + + let mut context = X509StoreContext::new().unwrap(); + assert!(context + .init(&store, &cert, &chain, |c| c.verify_cert()) + .unwrap()); + assert!(context + .init(&store, &cert, &chain, |c| c.verify_cert()) + .unwrap()); +} + +#[test] +fn test_verify_fails() { + let cert = include_bytes!("../../test/cert.pem"); + let cert = X509::from_pem(cert).unwrap(); + let ca = include_bytes!("../../test/alt_name_cert.pem"); + let ca = X509::from_pem(ca).unwrap(); + let chain = Stack::new().unwrap(); + + let mut store_bldr = X509StoreBuilder::new().unwrap(); + store_bldr.add_cert(ca).unwrap(); + let store = store_bldr.build(); + + let mut context = X509StoreContext::new().unwrap(); + assert!(!context + .init(&store, &cert, &chain, |c| c.verify_cert()) + .unwrap()); +} + +#[test] +fn test_save_subject_der() { + let cert = include_bytes!("../../test/cert.pem"); + let cert = X509::from_pem(cert).unwrap(); + + let der = cert.subject_name().to_der().unwrap(); + println!("der: {:?}", der); + assert!(!der.is_empty()); +} + +#[test] +fn test_load_subject_der() { + // The subject from ../../test/cert.pem + const SUBJECT_DER: &[u8] = &[ + 48, 90, 49, 11, 48, 9, 6, 3, 85, 4, 6, 19, 2, 65, 85, 49, 19, 48, 17, 6, 3, 85, 4, 8, 12, + 10, 83, 111, 109, 101, 45, 83, 116, 97, 116, 101, 49, 33, 48, 31, 6, 3, 85, 4, 10, 12, 24, + 73, 110, 116, 101, 114, 110, 101, 116, 32, 87, 105, 100, 103, 105, 116, 115, 32, 80, 116, + 121, 32, 76, 116, 100, 49, 19, 48, 17, 6, 3, 85, 4, 3, 12, 10, 102, 111, 111, 98, 97, 114, + 46, 99, 111, 109, + ]; + X509Name::from_der(SUBJECT_DER).unwrap(); +} diff --git a/crates/boring/src/x509/verify.rs b/crates/boring/src/x509/verify.rs new file mode 100644 index 000000000..8bc17a587 --- /dev/null +++ b/crates/boring/src/x509/verify.rs @@ -0,0 +1,87 @@ +use crate::ffi; +use foreign_types::ForeignTypeRef; +use libc::c_uint; +use std::net::IpAddr; + +use crate::cvt; +use crate::error::ErrorStack; + +bitflags! { + /// Flags used to check an `X509` certificate. + #[derive(Debug, PartialEq, Eq, Clone, Copy, PartialOrd, Ord, Hash)] + pub struct X509CheckFlags: c_uint { + const ALWAYS_CHECK_SUBJECT = ffi::X509_CHECK_FLAG_ALWAYS_CHECK_SUBJECT as _; + const NO_WILDCARDS = ffi::X509_CHECK_FLAG_NO_WILDCARDS as _; + const NO_PARTIAL_WILDCARDS = ffi::X509_CHECK_FLAG_NO_PARTIAL_WILDCARDS as _; + const MULTI_LABEL_WILDCARDS = ffi::X509_CHECK_FLAG_MULTI_LABEL_WILDCARDS as _; + const SINGLE_LABEL_SUBDOMAINS = ffi::X509_CHECK_FLAG_SINGLE_LABEL_SUBDOMAINS as _; + const NEVER_CHECK_SUBJECT = ffi::X509_CHECK_FLAG_NEVER_CHECK_SUBJECT as _; + + #[deprecated(since = "0.10.6", note = "renamed to NO_WILDCARDS")] + const FLAG_NO_WILDCARDS = ffi::X509_CHECK_FLAG_NO_WILDCARDS as _; + } +} + +foreign_type_and_impl_send_sync! { + type CType = ffi::X509_VERIFY_PARAM; + fn drop = ffi::X509_VERIFY_PARAM_free; + + /// Adjust parameters associated with certificate verification. + pub struct X509VerifyParam; +} + +impl X509VerifyParamRef { + /// Set the host flags. + /// + /// This corresponds to [`X509_VERIFY_PARAM_set_hostflags`]. + /// + /// [`X509_VERIFY_PARAM_set_hostflags`]: https://www.openssl.org/docs/man1.1.0/crypto/X509_VERIFY_PARAM_set_hostflags.html + pub fn set_hostflags(&mut self, hostflags: X509CheckFlags) { + unsafe { + ffi::X509_VERIFY_PARAM_set_hostflags(self.as_ptr(), hostflags.bits()); + } + } + + /// Set the expected DNS hostname. + /// + /// This corresponds to [`X509_VERIFY_PARAM_set1_host`]. + /// + /// [`X509_VERIFY_PARAM_set1_host`]: https://www.openssl.org/docs/man1.1.0/crypto/X509_VERIFY_PARAM_set1_host.html + pub fn set_host(&mut self, host: &str) -> Result<(), ErrorStack> { + unsafe { + cvt(ffi::X509_VERIFY_PARAM_set1_host( + self.as_ptr(), + host.as_ptr() as *const _, + host.len(), + )) + .map(|_| ()) + } + } + + /// Set the expected IPv4 or IPv6 address. + /// + /// This corresponds to [`X509_VERIFY_PARAM_set1_ip`]. + /// + /// [`X509_VERIFY_PARAM_set1_ip`]: https://www.openssl.org/docs/man1.1.0/crypto/X509_VERIFY_PARAM_set1_ip.html + pub fn set_ip(&mut self, ip: IpAddr) -> Result<(), ErrorStack> { + unsafe { + let mut buf = [0; 16]; + let len = match ip { + IpAddr::V4(addr) => { + buf[..4].copy_from_slice(&addr.octets()); + 4 + } + IpAddr::V6(addr) => { + buf.copy_from_slice(&addr.octets()); + 16 + } + }; + cvt(ffi::X509_VERIFY_PARAM_set1_ip( + self.as_ptr(), + buf.as_ptr() as *const _, + len, + )) + .map(|_| ()) + } + } +} diff --git a/crates/boring/test/alt_name_cert.pem b/crates/boring/test/alt_name_cert.pem new file mode 100644 index 000000000..d9e9f90e1 --- /dev/null +++ b/crates/boring/test/alt_name_cert.pem @@ -0,0 +1,22 @@ +-----BEGIN CERTIFICATE----- +MIIDsDCCApigAwIBAgIBATANBgkqhkiG9w0BAQsFADBFMQswCQYDVQQGEwJBVTET +MBEGA1UECAwKU29tZS1TdGF0ZTEhMB8GA1UECgwYSW50ZXJuZXQgV2lkZ2l0cyBQ +dHkgTHRkMB4XDTE4MDExNTExMDcwM1oXDTI4MDExMzExMDcwM1owfDELMAkGA1UE +BhMCVVMxCzAJBgNVBAgMAk5ZMREwDwYDVQQHDAhOZXcgWW9yazEVMBMGA1UECgwM +RXhhbXBsZSwgTExDMTYwNAYDVQQDDC1FeGFtcGxlIENvbXBhbnkvZW1haWxBZGRy +ZXNzPXRlc3RAZXhhbXBsZS5jb20wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEK +AoIBAQCo9CWMRLMXo1CF/iORh9B4NhtJF/8tR9PlG95sNvyWuQQ/8jfev+8zErpl +xfLkt0pJqcoiZG8g9NU0kU6o5T+/1QgZclCAoZaS0Jqxmoo2Yk/1Qsj16pnMBc10 +uSDk6V9aJSX1vKwONVNSwiHA1MhX+i7Wf7/K0niq+k7hOkhleFkWgZtUq41gXh1V +fOugka7UktYnk9mrBbAMjmaloZNn2pMMAQxVg4ThiLm3zvuWqvXASWzUZc7IAd1G +bN4AtDuhs252eqE9E4iTHk7F14wAS1JWqv666hReGHrmZJGx0xQTM9vPD1HN5t2U +3KTfhO/mTlAUWVyg9tCtOzboKgs1AgMBAAGjdDByMAkGA1UdEwQCMAAwCwYDVR0P +BAQDAgWgMFgGA1UdEQRRME+CC2V4YW1wbGUuY29thwR/AAABhxAAAAAAAAAAAAAA +AAAAAAABgRB0ZXN0QGV4YW1wbGUuY29thhZodHRwOi8vd3d3LmV4YW1wbGUuY29t +MA0GCSqGSIb3DQEBCwUAA4IBAQAx14G99z/MnSbs8h5jSos+dgLvhc2IQB/3CChE +hPyELc7iyw1iteRs7bS1m2NZx6gv6TZ6VydDrK1dnWSatQ7sskXTO+zfC6qjMwXl +IV+u7T8EREwciniIA82d8GWs60BGyBL3zp2iUOr5ULG4+c/S6OLdlyJv+fDKv+Xo +fKv1UGDi5rcvUBikeNkpEPTN9UsE9/A8XJfDyq+4RKuDW19EtzOOeVx4xpHOMnAy +VVAQVMKJzhoXtLF4k2j409na+f6FIcZSBet+plmzfB+WZNIgUUi/7MQIXOFQRkj4 +zH3SnsPm/IYpJzlH2vHhlqIBdaSoTWpGVWPq7D+H8OS3mmXF +-----END CERTIFICATE----- diff --git a/crates/boring/test/cert.pem b/crates/boring/test/cert.pem new file mode 100644 index 000000000..032fe60e9 --- /dev/null +++ b/crates/boring/test/cert.pem @@ -0,0 +1,19 @@ +-----BEGIN CERTIFICATE----- +MIIDGzCCAgMCCQCHcfe97pgvpTANBgkqhkiG9w0BAQsFADBFMQswCQYDVQQGEwJB +VTETMBEGA1UECAwKU29tZS1TdGF0ZTEhMB8GA1UECgwYSW50ZXJuZXQgV2lkZ2l0 +cyBQdHkgTHRkMB4XDTE2MDgxNDE3MDAwM1oXDTI2MDgxMjE3MDAwM1owWjELMAkG +A1UEBhMCQVUxEzARBgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoMGEludGVybmV0 +IFdpZGdpdHMgUHR5IEx0ZDETMBEGA1UEAwwKZm9vYmFyLmNvbTCCASIwDQYJKoZI +hvcNAQEBBQADggEPADCCAQoCggEBAKj0JYxEsxejUIX+I5GH0Hg2G0kX/y1H0+Ub +3mw2/Ja5BD/yN96/7zMSumXF8uS3SkmpyiJkbyD01TSRTqjlP7/VCBlyUIChlpLQ +mrGaijZiT/VCyPXqmcwFzXS5IOTpX1olJfW8rA41U1LCIcDUyFf6LtZ/v8rSeKr6 +TuE6SGV4WRaBm1SrjWBeHVV866CRrtSS1ieT2asFsAyOZqWhk2fakwwBDFWDhOGI +ubfO+5aq9cBJbNRlzsgB3UZs3gC0O6GzbnZ6oT0TiJMeTsXXjABLUlaq/rrqFF4Y +euZkkbHTFBMz288PUc3m3ZTcpN+E7+ZOUBRZXKD20K07NugqCzUCAwEAATANBgkq +hkiG9w0BAQsFAAOCAQEASvYHuIl5C0NHBELPpVHNuLbQsDQNKVj3a54+9q1JkiMM +6taEJYfw7K1Xjm4RoiFSHpQBh+PWZS3hToToL2Zx8JfMR5MuAirdPAy1Sia/J/qE +wQdJccqmvuLkLTSlsGbEJ/LUUgOAgrgHOZM5lUgIhCneA0/dWJ3PsN0zvn69/faY +oo1iiolWiIHWWBUSdr3jM2AJaVAsTmLh00cKaDNk37JB940xConBGSl98JPrNrf9 +dUAiT0iIBngDBdHnn/yTj+InVEFyZSKrNtiDSObFHxPcxGteHNrCPJdP1e+GqkHp +HJMRZVCQpSMzvHlofHSNgzWV1MX5h1CP4SGZdBDTfA== +-----END CERTIFICATE----- diff --git a/crates/boring/test/certs.pem b/crates/boring/test/certs.pem new file mode 100644 index 000000000..9d3516620 --- /dev/null +++ b/crates/boring/test/certs.pem @@ -0,0 +1,40 @@ +-----BEGIN CERTIFICATE----- +MIIDGzCCAgMCCQCHcfe97pgvpTANBgkqhkiG9w0BAQsFADBFMQswCQYDVQQGEwJB +VTETMBEGA1UECAwKU29tZS1TdGF0ZTEhMB8GA1UECgwYSW50ZXJuZXQgV2lkZ2l0 +cyBQdHkgTHRkMB4XDTE2MDgxNDE3MDAwM1oXDTI2MDgxMjE3MDAwM1owWjELMAkG +A1UEBhMCQVUxEzARBgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoMGEludGVybmV0 +IFdpZGdpdHMgUHR5IEx0ZDETMBEGA1UEAwwKZm9vYmFyLmNvbTCCASIwDQYJKoZI +hvcNAQEBBQADggEPADCCAQoCggEBAKj0JYxEsxejUIX+I5GH0Hg2G0kX/y1H0+Ub +3mw2/Ja5BD/yN96/7zMSumXF8uS3SkmpyiJkbyD01TSRTqjlP7/VCBlyUIChlpLQ +mrGaijZiT/VCyPXqmcwFzXS5IOTpX1olJfW8rA41U1LCIcDUyFf6LtZ/v8rSeKr6 +TuE6SGV4WRaBm1SrjWBeHVV866CRrtSS1ieT2asFsAyOZqWhk2fakwwBDFWDhOGI +ubfO+5aq9cBJbNRlzsgB3UZs3gC0O6GzbnZ6oT0TiJMeTsXXjABLUlaq/rrqFF4Y +euZkkbHTFBMz288PUc3m3ZTcpN+E7+ZOUBRZXKD20K07NugqCzUCAwEAATANBgkq +hkiG9w0BAQsFAAOCAQEASvYHuIl5C0NHBELPpVHNuLbQsDQNKVj3a54+9q1JkiMM +6taEJYfw7K1Xjm4RoiFSHpQBh+PWZS3hToToL2Zx8JfMR5MuAirdPAy1Sia/J/qE +wQdJccqmvuLkLTSlsGbEJ/LUUgOAgrgHOZM5lUgIhCneA0/dWJ3PsN0zvn69/faY +oo1iiolWiIHWWBUSdr3jM2AJaVAsTmLh00cKaDNk37JB940xConBGSl98JPrNrf9 +dUAiT0iIBngDBdHnn/yTj+InVEFyZSKrNtiDSObFHxPcxGteHNrCPJdP1e+GqkHp +HJMRZVCQpSMzvHlofHSNgzWV1MX5h1CP4SGZdBDTfA== +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIDXTCCAkWgAwIBAgIJAOIvDiVb18eVMA0GCSqGSIb3DQEBCwUAMEUxCzAJBgNV +BAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEwHwYDVQQKDBhJbnRlcm5ldCBX +aWRnaXRzIFB0eSBMdGQwHhcNMTYwODE0MTY1NjExWhcNMjYwODEyMTY1NjExWjBF +MQswCQYDVQQGEwJBVTETMBEGA1UECAwKU29tZS1TdGF0ZTEhMB8GA1UECgwYSW50 +ZXJuZXQgV2lkZ2l0cyBQdHkgTHRkMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB +CgKCAQEArVHWFn52Lbl1l59exduZntVSZyDYpzDND+S2LUcO6fRBWhV/1Kzox+2G +ZptbuMGmfI3iAnb0CFT4uC3kBkQQlXonGATSVyaFTFR+jq/lc0SP+9Bd7SBXieIV +eIXlY1TvlwIvj3Ntw9zX+scTA4SXxH6M0rKv9gTOub2vCMSHeF16X8DQr4XsZuQr +7Cp7j1I4aqOJyap5JTl5ijmG8cnu0n+8UcRlBzy99dLWJG0AfI3VRJdWpGTNVZ92 +aFff3RpK3F/WI2gp3qV1ynRAKuvmncGC3LDvYfcc2dgsc1N6Ffq8GIrkgRob6eBc +klDHp1d023Lwre+VaVDSo1//Y72UFwIDAQABo1AwTjAdBgNVHQ4EFgQUbNOlA6sN +XyzJjYqciKeId7g3/ZowHwYDVR0jBBgwFoAUbNOlA6sNXyzJjYqciKeId7g3/Zow +DAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEAVVaR5QWLZIRR4Dw6TSBn +BQiLpBSXN6oAxdDw6n4PtwW6CzydaA+creiK6LfwEsiifUfQe9f+T+TBSpdIYtMv +Z2H2tjlFX8VrjUFvPrvn5c28CuLI0foBgY8XGSkR2YMYzWw2jPEq3Th/KM5Catn3 +AFm3bGKWMtGPR4v+90chEN0jzaAmJYRrVUh9vea27bOCn31Nse6XXQPmSI6Gyncy +OAPUsvPClF3IjeL1tmBotWqSGn1cYxLo+Lwjk22A9h6vjcNQRyZF2VLVvtwYrNU3 +mwJ6GCLsLHpwW/yjyvn8iEltnJvByM/eeRnfXV6WDObyiZsE/n6DxIRJodQzFqy9 +GA== +-----END CERTIFICATE----- diff --git a/crates/boring/test/cms.p12 b/crates/boring/test/cms.p12 new file mode 100644 index 0000000000000000000000000000000000000000..c4f96b6996e41e31fb19f982c59a8a0fad90462b GIT binary patch literal 1709 zcmY+DdpHvcAIEpIHn+LVn7c`1ZjY9B3L8 zjw|#n{HOuk1r&0iF%S;)#10O_bHLQUBB+o9_T5255D>7l2!AC&yfIYuKOfYAP`oAt z=CNcSqgcI!0fAtEd=6NvkuSk#s^qLM4fYzT)`$021R*6ukUX`nrU1JEH}>)EcXsPU zg-pBTda9))RCK2vRGlO@C4X=}LG)fw)ak9vetvfTIiVe5fs44L(O_&^APyl<8H2um z()M+}wK5_tWSivIzB1+y{08i}oay!IU`&+gG~RO}#Y-U1Om0q}hW={fW^6h#v{~xx z8S5N`?hw^s`;}VGNFyH&Ugsls9JM($cUxB6`iY3xFnW-fXN%$rCE*tOXX*8mD@R7- zcgH-zWng89$T`JdgjDip{Gy zpX^#5_(y<8O4lQ&nNiSzxI1mJC1b93beM~-0;!)K43}<3}rqxg-&UbyI3c!nC!e%y2r)*oJKjT9i>8QHCsB0kjp(^Fd{UX%*W9lcE>ljVg@lT`mjduj) z*)8@MFr9zVW;5%?=IMUsi}gTWf+p=5t!`I8D51Hg?Vhu8;=8wTKMMrcbgI3KS~5=Z z#YF^~eM78DG>KZ1Mpa*29`^2g;|)YdM@;Trg(Y5uS13P5!}-2a&5^;b!>tR`iScVQ z-5+}jLH(8aXe+(#{f1S@qJ`z%*dyg+?e{paXhnR4Hh>0azO7O9FTkm&+p7ERPDbysRBVeVdD-I{mnA)7t1=+ z^B=lGy?9?N+d0oaU_0b)ax1tmaZ2e|nTyw{yclv)YX9A`y16cePjcf=)i)yUoME(% zGLgM%MVXVdm{7ydL1GpkeB6)M3iB|tKDyMhIFgZG$IY6?jXG+?Rc2UaxhLInU#r0( z`AKvD%1qQJ&Pz7nYX_A{*G>K0jKWqJgPfLvaXJn&r}hH8EFdxx1|)I2uWB@|Ell>O zdJaBu!JG;>RLkA6HJiB5G<=XW+}0Cv=<`h+fwZMNeu$-QuuA{n@0PA5dOw1=Q8rjj zQ9qwYW}%}ZW|AR1^SR4Bl7Cb&ys7EO>eGyy^VpYC0O}*LfnF)&e!z;U^GQI}Dwc!K z3#b*jYdfs!L?;K}LhWA8QGeBcqaB?`mA+-n%#J zl~ZoBX$$H+!}IhG8w!9;H96`CvrrqW)rt<$?Rgi=HHTkdI@V=u z>1Yq|sZ~AR*Uk16r)P(G62i=6>5VMYZvE~a4EG!KD%im{VY)2pbMm6ya}y82?Ua3p zhv$X{mvT=8zF%oa6cofy2*T#Dwfpn+Sdzj8Zt3+bvYS*~IAJ^n5-x_R0LMRo0 z*RJi%s2aTo%o-wA4;W4_x0Z;fbmW`+!GK1S1>L6m)4Q{(F>n55&vpxtNi7g*_sSz% z>RuhWweS)?H)7~#ATZgSWP1ep$hX(>7A<5s$3U~Cj)!5a;n+ijz$zd(576gUcP4Q3 z9QMrEL}XoK>)X4QWhHyrP1Ox9Ni0b<6g3bA33Bo95k8Ii+zvawsseGB7vx zG8i;=GBq|b91ixXOq=@5P_p*Z{*#&#i#~3ZJixg5*=_$P7iAJ;+nD`Uu_yjsdvVS6 zO}84OSf1#Ko%?t(ZPTQ4euL*)v6 z{2w!J@V~k2me-m_xvs!&(Fm?xT(i7({F*E3xc@@cq3p9KYfmdxGBGnUFfI-@2sDrd zhPf;six`WDZQI)&OWtkB6}P{#&E7M4+S3PZiU#r^X=N4(1F;6|3iv?^gc%wCv#=U4 z11V%b17n^U=vh`@iLz7ean;Xlr)T>0)rr*|(A&(qcs+CQvaBLCf%Yq+OH^_~nm+2N zzURr`6Y=@W+j-lRPJa+zHR+az%5>*XtSgpU`RXn6wf->mf0yT?-O+#E`UbVG@VG2+ tW?ACCKBeQVC4U&%Q=dw1?Y^TnvwffI2CFTpMrSMo9ruMg_=E&x0|0_v@QnZf literal 0 HcmV?d00001 diff --git a/crates/boring/test/dhparams.pem b/crates/boring/test/dhparams.pem new file mode 100644 index 000000000..6e4d4c68c --- /dev/null +++ b/crates/boring/test/dhparams.pem @@ -0,0 +1,8 @@ +-----BEGIN DH PARAMETERS----- +MIIBCAKCAQEAh3Betv+hf5jNsOmGXU8oxuABD2B8r0yU8FVgjnCZBSVo61qJ0A2d +J6r8rYKbjtolnrZN/V4IPSzYvxurHbu8nbiFVyhOySPchI2Fu+YT/HsSe/0MH9bW +gJTNzmutWoy9VxtWLCmXnOSZHep3MZ1ZNimno6Kh2qQ7VJr0+KF8GbxUKOPv4SqK +NBwouIQXFc0pE9kGhcGKbr7TnHhyJFCRLNP1OVDQZbcoKjk1Vh+5sy7vM2VUTQmM +yOToT2LEZVAUJXNumcYMki9MIwfYCwYZbNt0ZEolyHzUEesuyHfU1eJd6+sKEjUz +5GteQIR7AehxZIS+cytu7BXO7B0owLJ2awIBAg== +-----END DH PARAMETERS----- diff --git a/crates/boring/test/dsa.pem b/crates/boring/test/dsa.pem new file mode 100644 index 000000000..9b5501848 --- /dev/null +++ b/crates/boring/test/dsa.pem @@ -0,0 +1,12 @@ +-----BEGIN DSA PRIVATE KEY----- +MIIBuwIBAAKBgQCkKe/jtYKJNQafaE7kg2aaJOEPUV0Doi451jkXHp5UfLh6+t42 +eabSGkE9WBAlILgaB8yHckLe9+zozN39+SUDp94kb2r38/8w/9Ffhbsep9uiyOj2 +ZRQur6SkpKQDKcnAd6IMZXZcvdSgPC90A6qraYUZKq7Csjn63gbC+IvXHwIVAIgS +PE43lXD8/rGYxos4cxCgGGAxAoGASMV56WhLvVQtWMVI36WSIxbZnC2EsnNIKeVW +yXnP/OmPJ2mdezG7i1alcwsO2TnSLbvjvGPlyzIqZzHvWC8EmDqsfbU+n8we/Eal +sm5nloC8m9ECWpbTzbNdvrAAj9UPVWjcDwg7grAGGysh6lGbBv5P+4zL/niq1UiE +LnKcifgCgYEAo6mAasO0+MVcu8shxxUXXNeTLsZ8NB/BIx9EZ/dzE23ivNW8dq1A +eecAAYhssI2m/CspQvyKw+seCvg4FccxJgB3+mGOe+blFHwO3eAwoyRn/t3DZDHh +FjxKKRsQdy4BkZv+vhTyIYYCw0iPZ5Wfln+pyGGTveIDED1MPG+J6c8CFCJAUlEl +4nHvbC15xLXXpd46zycY +-----END DSA PRIVATE KEY----- diff --git a/crates/boring/test/dsa.pem.pub b/crates/boring/test/dsa.pem.pub new file mode 100644 index 000000000..ae298d093 --- /dev/null +++ b/crates/boring/test/dsa.pem.pub @@ -0,0 +1,12 @@ +-----BEGIN PUBLIC KEY----- +MIIBtzCCASsGByqGSM44BAEwggEeAoGBAKQp7+O1gok1Bp9oTuSDZpok4Q9RXQOi +LjnWORcenlR8uHr63jZ5ptIaQT1YECUguBoHzIdyQt737OjM3f35JQOn3iRvavfz +/zD/0V+Fux6n26LI6PZlFC6vpKSkpAMpycB3ogxldly91KA8L3QDqqtphRkqrsKy +OfreBsL4i9cfAhUAiBI8TjeVcPz+sZjGizhzEKAYYDECgYBIxXnpaEu9VC1YxUjf +pZIjFtmcLYSyc0gp5VbJec/86Y8naZ17MbuLVqVzCw7ZOdItu+O8Y+XLMipnMe9Y +LwSYOqx9tT6fzB78RqWybmeWgLyb0QJaltPNs12+sACP1Q9VaNwPCDuCsAYbKyHq +UZsG/k/7jMv+eKrVSIQucpyJ+AOBhQACgYEAo6mAasO0+MVcu8shxxUXXNeTLsZ8 +NB/BIx9EZ/dzE23ivNW8dq1AeecAAYhssI2m/CspQvyKw+seCvg4FccxJgB3+mGO +e+blFHwO3eAwoyRn/t3DZDHhFjxKKRsQdy4BkZv+vhTyIYYCw0iPZ5Wfln+pyGGT +veIDED1MPG+J6c8= +-----END PUBLIC KEY----- diff --git a/crates/boring/test/dsaparam.pem b/crates/boring/test/dsaparam.pem new file mode 100644 index 000000000..53bda543b --- /dev/null +++ b/crates/boring/test/dsaparam.pem @@ -0,0 +1,9 @@ +-----BEGIN DSA PARAMETERS----- +MIIBHgKBgQCkKe/jtYKJNQafaE7kg2aaJOEPUV0Doi451jkXHp5UfLh6+t42eabS +GkE9WBAlILgaB8yHckLe9+zozN39+SUDp94kb2r38/8w/9Ffhbsep9uiyOj2ZRQu +r6SkpKQDKcnAd6IMZXZcvdSgPC90A6qraYUZKq7Csjn63gbC+IvXHwIVAIgSPE43 +lXD8/rGYxos4cxCgGGAxAoGASMV56WhLvVQtWMVI36WSIxbZnC2EsnNIKeVWyXnP +/OmPJ2mdezG7i1alcwsO2TnSLbvjvGPlyzIqZzHvWC8EmDqsfbU+n8we/Ealsm5n +loC8m9ECWpbTzbNdvrAAj9UPVWjcDwg7grAGGysh6lGbBv5P+4zL/niq1UiELnKc +ifg= +-----END DSA PARAMETERS----- diff --git a/crates/boring/test/identity.p12 b/crates/boring/test/identity.p12 new file mode 100644 index 0000000000000000000000000000000000000000..d16abb8c70627bd959667495e578fb6f010a7f1d GIT binary patch literal 3386 zcmY+Fc{CIb*T-kZ%-F^$D#rP(jjrM6DXq;ULSwJ0;C7#;$Z7w z9PHO$c@c@DfAsGWG#5wj^;dEO0RexD!@nm$qyUuZe?P#0P$Vmufe?7XI$4!A4gxU% zusC`o{$ASWtHZ!%6&Yr};{6u`2KLv+>Oc-hrRqJ_6t51LvyFjb*MY2v@UpI}BEGHo zvK<0`n{R9EEh2758@vOSP&dDqOy0%>n)6OY2s1K@{ZZ4dUib$1=yGu{qy4)68IXNX zkk#_0j(a5M*dt*Sb*_j1P%B2^j47C!ky#GS(OVUXx=1c`4$i|hrFN!o2AI7yk$-hcE5vQ_4v$r`H0%4V>8 z@eO82W_dR!EPHOKwd{tqnGp6|1N4%pQ^bZ&wmM^1xKK`mmp5~Z!-*jOxzzTtyjI@N znR!#3*mi1n<$H_gljnHMOm{8 zY+OW$1dI#%nC00mv=w|&H?Ds_&Q&>zV$~?as>se&LcGBkC+KuH))CEF+bqRg55Y-3 z?~vHFBT!PxQJ=a_tK7s`O43jI#enGN$Sir<+d_5iA)Nn{pocY~r%FKRo?P!z?Yz^^ltS`1rG&W|$}6 zIK1R;ZV;$x`_4!Z3O$S$y|fdG=LX_+wEsUm|gb|J|R6=$2&5w5;*9HoUf+ zLC&xB@i{S7wciFKq`HokLs&MYUDh;a#&Nq`>{RQv)JdX8iEVvb{5|aGL9i^^AOpXY z&L6LF6u+99$t7+kFys94TO$8LR-yenXgMarH6$3GlTQl|GYr#AJ*J8<*w1~fu~s1O zdhUXpvVUHaLYgm_R_B=VPu;^q1k37|S#Az*#kI@%`yAGFcpg*|9J$55Dn2i3qil7T z#{o*c=&(?Um6=WCnJ<;C^yU0Wn5+U;LCTeF5LRZG`&7h5u>K>DCz^!_kAQ+Uk_>p^ZJ-AG#sMX^}swaF9O348d8KL2>E z)<=Onr|{6URV7{c-I=%8nti`|U(Z3YP74t0c_K)3>xS6vW%e^B29#WNrqLHAUIEy{ zzB?q&G}1_h*%0C4?15@moi|7#SL*3DFn*TcNo)PGTXh0X35{``dR4uHIDDveSU-f9 z{K;uqKl-Nv+V4mA(kBrDEVii_A1b!KJS}U4Iyn5L=6`(p(ctU_de;X1DiO>4u2__k zVlWt+97!S9u=F;5c}*O*ufnS{X8;m3K*h#T0PXmn;cX%gWvELfIb5GGobB|9yhDDpE&NP`u@Gnp&0 zQZLqLDBx6TpRdU#?(psb%LU%s+uHH3^q4!9CoL-jgzIVo56|~LFj*Dya8o&rW&R9f z#MCF&8DNgw4~2m6o|%0c4_>q*LnYa8v7A*R-OG2mtCse5WW>N06;mbHTJR0ly~=vb zjkL_*lL_>a|0oF63*HEu&>67XeZie|`a^`89-;zuIMvx0wZDKHFrH{;{>A+ay$ZLY zQ&)*|(POvl>U&=z{Tg-ooGiyts!|CuF0TS^0VgtL{JJ0I`v@^G`!EHbzFi}2 zL*x~uu?J$3fnQa7=3r+w1G;jan4eqgmvr-O)nlM%Da;H8qk?wE67l%lcqMyzw_r@o02)63je6Xu1-n29!*PLHn-90_SxMGSU5yp=aUNgAPf&JU@M3Mr*>n3*Qfs4@UZdr)3<49u!j(8;{>P^eE^Osav(h>l?db zz-D`eK9#oSSADv+c=1$Wl1_5pV%p++fJluaNC@?&3cRf~mUihw;wH~1NpD-OAhEp@ zp%J>UkR&rBuE;-O(>>(RI-$%WT}_LVD?y;mD%UYD&1i4MAzBQ}1OKO=ov1 zqOb6W(VoB8*XL$(xwOFE$}X1Q4kfSMF15mhjE{%TXEK8mEZLjGl#c@Kdami3&$?8R z3X0QOt>y=BbRV!lHr4bxPL(_bvynK6$A17e7YDHh;~?gLW%S=PgfjmhZkd3fTpV}< zi36|x7lrBmq3~Tpke%T}RQEp=2IIiJs)bH-t;&|}Fh+NaTeU~I+z6U$_st%d4& z-GhEwwpii2MIE&MtZ#2+NiaN#V8qoDMFa~9WuL=dYyA0VF!^rHt`>`@lvQaI9rjF% zf>&A5%p>?lyx=A)lifEe-ZTF_&hx;HqdgC=j^D`xrXHl2F1=@bAN8)917)sJ6!*me z<7HAMfY#b&Kyj?9V{7jX0ex*Do!y1*jQ4BlZqzj@WpzG)?+b1vXK$aw@ws>CE|ZxA z5+_}aqAICBLaX0@Sx&A-B{8F6=5ieD+o4bn7mz)?DFYwS9ISfcSkc1D?^7F(MYzNFqebD~<*>|~&C#$*Y)t41%< zSUb^f@Dm)@x`dT(>NzTU>>Qbs5c=rJ4Yz`@*9~DI<0a2AHA?iN*a+E!i8J>{0~PG?gk>1>Zj*jW^JtpfIAm$MRWkXoi1Cv@Ar>b zH0;ah2{C4g3|27>74O!U!X+684ZU@I&-k@Ud2^qCUNAL{TZwyyN$G#HNDrFsP#65c zrxf~mk;jg7wV|ONPtWwMdR6?%)Ot`$mI?|@QaMOVx=?!cXeO8~Fz9+qIZ5WmPKHb7 zgofgUUSjO+ua`8j{?2gr-`{@-43inOgBoKao68B6iAOcPQPh6Q%T2XayOCFbynb>X zH$308=bjoRmehcbbLNXtZBq z0J~mAB-w(EFUlR4pRu`v?GEVIWM&zLpe{CcX2(DI&Sy^R84J751Y9rEzC(o6CO0>y z=E{ugtGsQie~J(y5De$j6$PK5iB=^dz9SE8W5!8={%vccv}UmILPt41Z;;(tc^)R{ zZ=k`tzDp0hSkCT9WhKp4h)^M!%VaxWo6p-gp4Xnu=HqXHv&7k?hh-$IK1 zzdJH=%5w>F0z3c@{)P*{8Q=$y1-Sn0@Bil#LY@oEY0E8>a&AS}wv{Gv=((Kjlgzys zg_K7MBN?F(X;wNA9|QpA+;x-8N-+3rFwTsLlk6hIQoAssV1}Dt>u@5F2fhDT>Hh%N CKSRy{ literal 0 HcmV?d00001 diff --git a/crates/boring/test/key.der b/crates/boring/test/key.der new file mode 100644 index 0000000000000000000000000000000000000000..6b6209fd1fbe809a335cf29b263d9ffd5d1b9da0 GIT binary patch literal 1193 zcmV;a1XlYnf&`@k0RRGm0RaH0^d*c$vlpXKh5jRvhtPO78%Y=cEl1Pk8{TX-{Fb={ zKk_%;zwa{=x@E=k(3-KDiZ)_T^+L$? z>Y2<1&2+gSWzNU}-9~KQ z0JJ-yvu<{Jp*<6blO9gR*Ngy5QdX+|y6O~O7<%Spk+IVh6EoY-4^hqL-IUy<--Pex zPEZtCT%h*Qtvfd8Dho9N0|5X50)hbn0HWjQgNX#C=#|Xr&A$|rAR_`nfqF|^vE_8` z2c;+Xic=e9axd|18_NU|`$ffBzVJy-gC)6avm8%*V7+ z1_t59C&GpC32iN`4t(E-NYO%S826Zkzq)RE-9;wc-Dij33EfeuV78P~IUh%vwBN$d zUuB8qY|+1c=rJpFKxztASvyT0VYFz>Ale5sRNp4S{nINLoMDnh=E1$>Sj%&We`-04 zD6-p!AX~h6p-Yy6u@-u^M}Wq};R1ny0NAAnWQ2}*Vp0Xd$s`2|+6&-Z^Jw64)*}Oe z_ZN-bl4Y`U<$d+gHhH?YTP@XHpnS=_BWd)T@pQ{zf1mZnNv|7%7taIPbaQBV@>vVq zlnF=rT5HUlPd}Js`tzJjjqL`sMzU8@_HXu73mwh=6n7FH11b$&pV_g%67N_VEdqgo z0LP_7AoFjmabS**yghHl8Mb3mflu0`YEQeF@TZ_gdtw zEc(=g$haaDlImCWw1;_$@)Tfl=wXrA&5!v9SK+2j=d}R*Us=Y3ml_81q}yMRZ0lo68qD3r%wQ}z zCh$?VD_8lNJ;)IZEa5cszT}I=-Vin!#_nK~-yQfqD9Qx}WJE1n9G8VxsXpFnHLng~ z0)c@5$3<8fBcg?ttnc3gA>8||Z7*>D~Kv+I$4zvy%An0)c@5lTMfFGT$Uqdt4*iAJD?I-!9|> zzMbamG^Li-7cR;pil@zMtqV5(6vNh9$_2EtOFf*w{_oidMMYJv9np`#NYn@sB~M&Z zz?Rudqk|Hf$ThqmY;O335u@G`JIt;LBKLY&wcpw~C?sGlQ0ondAf&n5h4F(A+hDe6@4FLfG1potr0S^E$f&mHwf&l>luU6MGn_40Ri}dwN z@vhb=Z^HB98^rxJ@#6tOl=Mn-(3yI4L%sqMDBLwcAdMQ8ond&)XdY^&MKU(RSH~_s@BghpE?S-IiLB>jW0gZG_YR$c2X>vw0s{d60bXc_hyVZp literal 0 HcmV?d00001 diff --git a/crates/boring/test/key.pem b/crates/boring/test/key.pem new file mode 100644 index 000000000..d381795d3 --- /dev/null +++ b/crates/boring/test/key.pem @@ -0,0 +1,28 @@ +-----BEGIN PRIVATE KEY----- +MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQCo9CWMRLMXo1CF +/iORh9B4NhtJF/8tR9PlG95sNvyWuQQ/8jfev+8zErplxfLkt0pJqcoiZG8g9NU0 +kU6o5T+/1QgZclCAoZaS0Jqxmoo2Yk/1Qsj16pnMBc10uSDk6V9aJSX1vKwONVNS +wiHA1MhX+i7Wf7/K0niq+k7hOkhleFkWgZtUq41gXh1VfOugka7UktYnk9mrBbAM +jmaloZNn2pMMAQxVg4ThiLm3zvuWqvXASWzUZc7IAd1GbN4AtDuhs252eqE9E4iT +Hk7F14wAS1JWqv666hReGHrmZJGx0xQTM9vPD1HN5t2U3KTfhO/mTlAUWVyg9tCt +OzboKgs1AgMBAAECggEBAKLj6IOJBKXolczpzb8UkyAjAkGBektcseV07gelJ/fk +3z0LuWPv5p12E/HlXB24vU2x/ikUbbP3eMsawRzDEahQqmNmPEkYAYUAy/Qpi9GN +DYvn3LqDec4jVgeQKS+p9H2DzUpTogp8zR2//yzbuWBg2+F//xh7vU0S0RQCziPM +x7RSBgbhxSfChfEJbS2sDnzfh0jRQmoY95iFv7puet1FJtzdZ4fgCd1RqmC2lFM5 +H0eZtN/Cz19lieVs0b996DErdEBqClVZO00eYbRozCDaBzRU3ybB/dMrGJxhkkXm +wb3kWMtziH9qOYsostuHIFu8eKFLloKxFnq2R4DGxOECgYEA2KUIZISOeGJSBcLJ +JAUK2gvgXPNo4HHWIwOA9xeN3ZJlsnPlffXQNnm6t1st1V2gfMm9I2n0m/F0y2B/ +n/XGSa8bghfPA9l0c2h58lkL3JQJR/paa8ycTz+YZPrznEyN7Qa0RrJXUvZv9lQL +Hc3+FHcSHgMqDV2f2bHAEu9YGi0CgYEAx6VEIPNvrHFgjo/jk1RTuk+m0xEWQsZL +Cs+izQMr2TaeJn8LG+93AvFuYn0J0nT3WuStLPrUg8i4IhSS6lf1tId5ivIZPm4r +YwMyblBJXhnHbk7Uqodjfw/3s6V2HAu++B7hTdyVr9DFuST9uv4m8bkPV8rfX1jE +I2rAPVWvgikCgYB+wNAQP547wQrMZBLbCDg5KwmyWJfb+b6X7czexOEz6humNTjo +YZHYzY/5B1fhpk3ntQD8X1nGg5caBvOk21+QbOtjShrM3cXMYCw5JvBRtitX+Zo9 +yBEMLOE0877ki8XeEDYZxu5gk98d+D4oygUGZEQtWxyXhVepPt5qNa8OYQKBgQDH +RVgZI6KFlqzv3wMh3PutbS9wYQ+9GrtwUQuIYe/0YSW9+vSVr5E0qNKrD28sV39F +hBauXLady0yvB6YUrjMbPFW+sCMuQzyfGWPO4+g3OrfqjFiM1ZIkE0YEU9Tt7XNx +qTDtTI1D7bhNMnTnniI1B6ge0und+3XafAThs5L48QKBgQCTTpfqMt8kU3tcI9sf +0MK03y7kA76d5uw0pZbWFy7KI4qnzWutCzb+FMPWWsoFtLJLPZy//u/ZCUVFVa4d +0Y/ASNQIESVPXFLAltlLo4MSmsg1vCBsbviEEaPeEjvMrgki93pYtd/aOSgkYC1T +mEq154s5rmqh+h+XRIf7Au0SLw== +-----END PRIVATE KEY----- diff --git a/crates/boring/test/key.pem.pub b/crates/boring/test/key.pem.pub new file mode 100644 index 000000000..2a8225696 --- /dev/null +++ b/crates/boring/test/key.pem.pub @@ -0,0 +1,9 @@ +-----BEGIN PUBLIC KEY----- +MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAr1bXMptaIgOL9PVL8a7W +KG/C8+IbxP018eMBQZT0SnPQmXp0Q8Aai/F+AEDE7b5sO5U7WdxU4GRYw0wqkQNF +si78KNfoj2ZMlx6NRfl4UKuzrpGTPgQxuKDYedngPpWcbmW4P3zEL2Y7b18n9NJr +atRUzH1Zh/ReRO525Xadu58aviPw1Mzgse7cKyzb03Gll9noLnYNIIpO8jL+QyrD +8qNmfacmR20U0a6XDTtmsmk7AitGETICbTT0KRf+oAP0yIHoonllPpNLUEPZQjrp +ClS/S/wKdj7gaq9TaMbHULhFMjbCV8cuPu//rUAuWp3riaznZGOVQyn3Dp2CB3ad +yQIDAQAB +-----END PUBLIC KEY----- diff --git a/crates/boring/test/keystore-empty-chain.p12 b/crates/boring/test/keystore-empty-chain.p12 new file mode 100644 index 0000000000000000000000000000000000000000..c39930a5cb4f003f48ab94cc87f9435730540f24 GIT binary patch literal 2514 zcmY+Ec{tPy7sm%PYGy29l6{>Jnwto_x^F7^PKPZobP|CE5VLn=X=W_4eD5|T+WbPU?{wz^ zH>yNRB3)^xvxrw`6y9QzEe&Ir3xrv&)=9$}Ojm zp87&GAzImu81Q6Or6jiZBY>eoBA1%QB6d{Jv>u<1l`Cb;gsPFlEaz{~(}R~?&nn-@ zf``oK!#6k^Q(`9^iuLc2oos8z%QGJ^X{^DhauJ?wl}EB6o1v&Mt_^YYcyTgf=vJJm zOYzw5`(x0ZukQ!rnPyLhV+2AIeG;tdyecnz*875iCFW6Sd^cU=R9r@h*H0N)Ygf(B zT{~hs_tAQ? zpqnyo)W(i0oo#W8v>#H{aCl(CnniSv5GL~TgsSL>+!;~GPHcVhyxEg9HT!O3hQ#Jz zK!UCDrnL>b^@p-9j}2QYI6-PF<9Z6No+Db2n^T0^>y--ZWJ6qyHSx5N^6d3FuJCVUF?n@%oB|JI?3ZQbL*|$Ze;*FF>BxCJY-woQq6^ya!)N6N!p{3 z!p}`FRd28+K^8$xOVhFLEPed+(#WMRzZ+n^UayqqfFZmN<#=v(RdARpWpN^-x);v% zX|2z6%W-|~o5uJRyU3~uhxqS}h}x1=EDHuFy$F#x)DBNnS8Rak=hQa1vczEgV;uo^ zSuX504fh%!tBmaN>C4KVQ4k^Le9H~Zy4<|gI#2BL($kTOa}L|ePk8vCxsdT{tR3be(;s@>0W_9e_qr=&0gJjch7@p zd24}H+J)D<$vVY37Jlc;^mJwk?iu#&8V5x#Eb|$iMxciAU;hknf`l{<+zg+Ysx!SQ z^kRmxSvtV;CaARArawN8oL28z@o;wTU8uRq1f4E7lAp}q8u;!*Wt${UiZip!lK6ej zppxlTYMR=H#}LH)+%G6^Hr7gwrvsg}h5tEp|H3TePt2kl$o8!oe@SY%#wJnH#R#F)9L!J5Mgk0_ z+A>$>d{P6qA0&iEAz6Nc&ac8-BMdsViNc z08tj)MOm~O*mj`P?Ym0EGqfmP+JDX+=jO^c&>~w>S2gzeG5j>WP~>e8sp2 zni?~rZG{z0z4Ww`jr0upA9Zpk*(RmjMkjtY+yn=@WhkL~-CUicfURyv&_xO;N@y2l zW9c^7s2esbFg=+rD^iM&iACCCN{q&*M>yQ0ij9ZrgCNy+qo3NGU%M5a(9Li+<)d|CUPcXZR9SLr)CR-ZO`nPVgUY+B&=tKL3}*w9s)m zi{{pf19HH=mC%1br%b9fz;mjvhG?c{o4L`7=RTOzBpLhj5+vp9y5pHW#E1J+kK2cA z*IBTj>qeI|de>hfmXnWAvs%`Xr^`NV7`v8?UA zgketBHv*Dh9>yESCZu548vQ;#S@`kr)g`-TgTT2;(dRnyOrvts=GMt@2}07C`M4Z6 zCsol(_oTozF{<%d9croZo4Re&wF~brEk?8#RkTyy=BQ5@ZKJ`Cg~P5Cw<_%)F+F9t z7W-F)$LWOCeU)Aky!00+))0>#-*m{Vqb1qpLvDba}yh0 zTEHEbx;=aAPbHEM@_f`dRPP&LNQUrrZ9p8q5=T`0)hbn0JtfNIjnox z>EEI=Ze~SK13M49-d&$kBOt<3&9k9a0P|d7N?y+QwntJ~9B&0j9K}X56I#~`3NM1` zijm?B8cr@(UH_HUVDw8UKFe%}zZ`8voa9cbWO`B4X0zc;_5jB5^nlh$(JX$y;o2(q z%r;kpWn6ZxR=C43P$<%a6aTylIIYlH zhcB!{Yo7-WBc(6KT}xXyvxYeb6r3uxVF*<}4-Z5N*0e}X8f+$AR)gdZ?W4zYuw9lm zSh=iB-Z)+y6lK>35p3;F8hN;}epWBQA~<#Eeo1Cgwf}=orERjV>$m_Hn4n!7Ga^oHlPN2+)Wr>;42PUb?>=NZxT zjeQ+;^D^lSC6so=(5n9(`d+IpogQhjuA#=TKg4%ZDJKsLa?P%7#3uv|XGf%28;4EG zp;}ZrpH;4Krr$*(Y9$!2dkRNnV5OqRE#Z?q3Yt@UyvBy52{NbFmJ?|eJ73|F>V;%b zWxsBqMJ3XcJ3wO2l@A$id$Lr0*qOrPDs9Xgs;(eHR?(_TGi**ozq0w%v3UZ4fdKFE zveNS_lp3vd%z!GG%VE%FAzev9(3r;{A5xXenPm5xG2r@HAl{;ySHN%BD<%DI@()eL z^tlPheDzjClo_OydkeitoRkL);!+EoaPZ zJ+@;XDQT4sA^nFGyponqwQrPEs?P#}fdIsONX~`2n}Z|Z(C&M?C3rht`CT{`s8~O> zuUoHbN)KdM4}5MGc9E!iC~D|pf#OXB5sJo(N(ze>e}LryvX~}JjUD*-}fV3oPy_GH~yRDej;@2z*qw38D?fIoHY5+%*D$ zfNQ)6VDnN_(~hFw$%IzQs|J~L&7c798cF^@QX+IBQPpDPmT{5iQcsK_ZiGpBMmv7L zEaU9RF7+3Z6V5@^lbty!j0B{>y!09tj(I{jI3{SVt5);2^^l3EH(Azz%O^ix9`{ z0)c=KT!UxWihsu=KAC|eAa=48mnzoE1hwL6PR+@+7KI_sl8v6FPRO{OT18S!El|Q8 zC5FznQMucjl~-%Zev#MtzV7D{ycin(H79?PT%WmuOozLSGRkfm5L<2yT;?3~(q^k< e{i>d=u^m@tp6-Ai2kOFUIU(JPR;qtZnHmW{L`6ve literal 0 HcmV?d00001 diff --git a/crates/boring/test/pkcs8.der b/crates/boring/test/pkcs8.der new file mode 100644 index 0000000000000000000000000000000000000000..47a2db8ad99c55424b3eba09cf195d9a826d9f17 GIT binary patch literal 1298 zcmV+t1?~DUf&~sRKn4jahDe6@4FLrWFf%Y41_>&LNQU&4g?6H{RVY&!jdHd z0tf&w6b1+?hDe6@4Fd-R2r=!d)&+UZtptJu$a&yr7EYiZl}%pX)s+2=UWe8d(yTAgryfPqPHtHz;;ifXGYSsJTOHe zI(!>JUNs`2U?!0HCoRn17PBy|;%NW)$%T0+U3=Lk)uHBwtQG#BiZAiFspr*Y#Gwy5 zGg42zET6*^K33jH; zvxc`zbs}$E+1Zrx^EoZ7I?_`<1WO<%uQ`!y2DW*}zQPv=DJ2aNno8b4{lR>aoG#-& zISxgK?dIbRXXCU)N6N{9YWlcnL2qC*_p8v+#EeJL8wvNwUv+Ho^C^3S+*D`!X#HK* zNT|q@IRB@1U7X8S4FxzyI3r3{gShC3{lvGvK}?kW4_2%xh@d!ppy-$^jxW!E^v@WO zU#-yV0a~)9TjWC+V@Y@d=F-Z5y9=oc)U?jDTsUgV-FrV)U{V|->seixdB?% zDJ?}s-jzNuXe+Z&Keo9AU6H^1`+}L%>*`d>yO<{mPg+Ki`CN&7am%+a2E9CGhh?@t z%)n;cRW0%gMLmykt(2+_$IH22606T=!BD&cMU`cx$HB08d7dzryn-T{V3foZ&fwkfAxTlY7JTX;!pY#?TqE zy<%dt>hj;;71!fhK2t-cHN1{mmS}OlH4QlquCHV||K1s#zRN0DN5loo%xB(NtrQ-I zK@As?EzP=W83$>T>dT32iOp(A2shg{-?K-FFcoqmT@9*IZ*}^C{Uo+kNGL5kIUKYC zn5#!5pEd&XST1fC33Ooy3gL(tpS`PKexi=nbJed=0QCYaqkEds9;Q5naavFIx<%dI IwLU>-bsH6IkN^Mx literal 0 HcmV?d00001 diff --git a/crates/boring/test/root-ca.key b/crates/boring/test/root-ca.key new file mode 100644 index 000000000..746ae2a68 --- /dev/null +++ b/crates/boring/test/root-ca.key @@ -0,0 +1,27 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIEpQIBAAKCAQEArVHWFn52Lbl1l59exduZntVSZyDYpzDND+S2LUcO6fRBWhV/ +1Kzox+2GZptbuMGmfI3iAnb0CFT4uC3kBkQQlXonGATSVyaFTFR+jq/lc0SP+9Bd +7SBXieIVeIXlY1TvlwIvj3Ntw9zX+scTA4SXxH6M0rKv9gTOub2vCMSHeF16X8DQ +r4XsZuQr7Cp7j1I4aqOJyap5JTl5ijmG8cnu0n+8UcRlBzy99dLWJG0AfI3VRJdW +pGTNVZ92aFff3RpK3F/WI2gp3qV1ynRAKuvmncGC3LDvYfcc2dgsc1N6Ffq8GIrk +gRob6eBcklDHp1d023Lwre+VaVDSo1//Y72UFwIDAQABAoIBAGZrnd/dC2kp11uq +Sg8SHk3GMdPPjTf/lq51sVJAU4fdV2Eso0XCiCzdKDcqR6F+jiu8jHp4YO0riW8N +b1pkjohGjyOaddIaaVsZ80/OkgDz20Ird9XQ7uoEODvopA12+755BDH5PDwqHVeM +nKfPiwAK6Jz6CxGO9bq9ZNoBiSyO1uofaB4Cpp8t74XVeAuPiI/Bb6WJ8TW5K5dt +x0Jihdo46QgZR+z4PnyWIoACkhSoQmtTb9NUrpKceBcxdCrZ/kEmYpnPq/PuSw6g +6HthjYP/H9Xulz69UR5Ez6z+1pU1rKFmQ46qK7X3zVHg233MlGekMzxdmShEjzCP +BMGYpQECgYEA5tqTZsUJwx3HDhkaZ/XOtaQqwOnZm9wPwTjGbV1t4+NUJzsl5gjP +ho+I8ZSGZ6MnNSh+ClpYhUHYBq0rTuAAYL2arcMOuOs1GrMmiZJbXm8zq8M7gYr5 +V99H/7akSx66WV/agPkLIvh/BWxlWgQcoVAIzZibbLUxr7Ye50pCLfECgYEAwDLn +mFz0mFMvGtaSp8RnTDTFCz9czCeDt0GujCxG1epdvtuxlg/S1QH+mGzA/AHkiu7z +uzCwGKWozNTdRkqVwYoJTB+AYHseSkuGP+a1zr39w+xBW/vESb2oP95GIwprXcG2 +b/qdeQVzuLQhYoqWI2u8CBwlHFfpQO4Bp2ea+ocCgYEAurIgLSfCqlpFpiAlG9hN +8NYwgU1d4E+LKj+JMd8yRO+PGh8amjub4X3pST5NqDjpN3Nk42iHWFWUqGmZsbM0 +ewg7tLUgDeqiStKBoxaK8AdMqWc9k5lZ53e6mZISsnHKUQdVBaLjH8gJqdAs8yyK +HudEB0mYwMSUxz6pJXIHrXECgYEAhJkaCpXm8chB8UQj/baUhZDKeI4IWZjRWHbq +Ey7g1+hPMMOk6yCTlf1ARqyRH8u2ftuIL5bRhs+Te21IE5yVYOb4rxn0mZuXNC6S +ujdTKwUMtESkeu9hZnaAQz/4J2ii1hY05WCDj+DhC4bKmY9/MYS8PuQb/kfwVqld +Xr8tvrUCgYEAmslHocXBUFXyRDkEOx/aKo+t9fPBr95PBZzFUt9ejrTP4PXsLa46 +3/PNOCGdrQxh5qHHcvLwR4bPL++Dj+qMUTJXANrArKPDpE2WqH6pqWIC6yaZvzUk +17QbpXR6bHcdJV045pWpw40UCStTocVynY1lBfOw8VqxBIBlpVBBzew= +-----END RSA PRIVATE KEY----- diff --git a/crates/boring/test/root-ca.pem b/crates/boring/test/root-ca.pem new file mode 100644 index 000000000..4ec2f5388 --- /dev/null +++ b/crates/boring/test/root-ca.pem @@ -0,0 +1,21 @@ +-----BEGIN CERTIFICATE----- +MIIDXTCCAkWgAwIBAgIJAOIvDiVb18eVMA0GCSqGSIb3DQEBCwUAMEUxCzAJBgNV +BAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEwHwYDVQQKDBhJbnRlcm5ldCBX +aWRnaXRzIFB0eSBMdGQwHhcNMTYwODE0MTY1NjExWhcNMjYwODEyMTY1NjExWjBF +MQswCQYDVQQGEwJBVTETMBEGA1UECAwKU29tZS1TdGF0ZTEhMB8GA1UECgwYSW50 +ZXJuZXQgV2lkZ2l0cyBQdHkgTHRkMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB +CgKCAQEArVHWFn52Lbl1l59exduZntVSZyDYpzDND+S2LUcO6fRBWhV/1Kzox+2G +ZptbuMGmfI3iAnb0CFT4uC3kBkQQlXonGATSVyaFTFR+jq/lc0SP+9Bd7SBXieIV +eIXlY1TvlwIvj3Ntw9zX+scTA4SXxH6M0rKv9gTOub2vCMSHeF16X8DQr4XsZuQr +7Cp7j1I4aqOJyap5JTl5ijmG8cnu0n+8UcRlBzy99dLWJG0AfI3VRJdWpGTNVZ92 +aFff3RpK3F/WI2gp3qV1ynRAKuvmncGC3LDvYfcc2dgsc1N6Ffq8GIrkgRob6eBc +klDHp1d023Lwre+VaVDSo1//Y72UFwIDAQABo1AwTjAdBgNVHQ4EFgQUbNOlA6sN +XyzJjYqciKeId7g3/ZowHwYDVR0jBBgwFoAUbNOlA6sNXyzJjYqciKeId7g3/Zow +DAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEAVVaR5QWLZIRR4Dw6TSBn +BQiLpBSXN6oAxdDw6n4PtwW6CzydaA+creiK6LfwEsiifUfQe9f+T+TBSpdIYtMv +Z2H2tjlFX8VrjUFvPrvn5c28CuLI0foBgY8XGSkR2YMYzWw2jPEq3Th/KM5Catn3 +AFm3bGKWMtGPR4v+90chEN0jzaAmJYRrVUh9vea27bOCn31Nse6XXQPmSI6Gyncy +OAPUsvPClF3IjeL1tmBotWqSGn1cYxLo+Lwjk22A9h6vjcNQRyZF2VLVvtwYrNU3 +mwJ6GCLsLHpwW/yjyvn8iEltnJvByM/eeRnfXV6WDObyiZsE/n6DxIRJodQzFqy9 +GA== +-----END CERTIFICATE----- diff --git a/crates/boring/test/rsa-encrypted.pem b/crates/boring/test/rsa-encrypted.pem new file mode 100644 index 000000000..a62499972 --- /dev/null +++ b/crates/boring/test/rsa-encrypted.pem @@ -0,0 +1,30 @@ +-----BEGIN RSA PRIVATE KEY----- +Proc-Type: 4,ENCRYPTED +DEK-Info: AES-128-CBC,E2F16153E2BA3D617285A68C896BA6AF + +vO9SnhtGjGe8pG1pN//vsONnvJr+DjU+lFCiSqGMPT7tezDnbehLfS+9kus2HV7r +HmI14JvVG9O7NpF7zMyBRlHYdWcCCWED9Yar0NsWN9419e5pMe/bqIXAzAiJbtT4 +OB9U5XF3m+349zjN1dVXPPLGRmMC1pcHAlofeb5nIUFTvUi5xcsbe1itGjgkkvHb +Bt8NioHTBun8kKrlsFQOuB55ylBU/eWG8DQBtvFOmQ7iWp0RnGQfh8k5e5rcZNpQ +fD9ygc7UVISl0xTrIG4IH15g34H+nrBauKtIPOpNPuXQPOMHCZv3XH8wnhrWHHwT +ZFnQBdXbSpQtMsRh0phG2G+VIlyCgSn4+CxjCJ+TgFtsoK/tU0unmRYc59QnTxxb +qkHYsPs3E0NApQAgH1ENEGl1M+FGLYQH7gftjc3ophBTeRA17sRmD7Y4QBInggsq +Gv6tImPVBdekAjz/Ls/EyMwjAvvrL5eAokqrIsAarGo+zmbJKHzknw2KUz2En0+k +YYaxB4oy9u7bzuQlvio6xYHJEb4K197bby4Dldmqv7YCCJBJwhOBAInMD687viKv +vcUwL8YuS6cW5E8MbvEENlY4+lvKKj3M8Bnyb79cYIPQe92EuCwXU9DZXPRMLwwM +oFEJpF5E/PmNJzu+B52ahHtDrh83WSx71fWqjdTqwkPZhAYo3ztsfFkb/UqUcq8u +rBSebeUjZh0XZ9B04eshZQ5vJUcXGtYIe/77beV3Pv89/fw+zTZjpiP9Q3sZALzf +Qt0YGp0/6qBuqR1tcqdu65AS2hun7yFw7uRavqYKvww4axRiz2do+xWmZFuoCAwD +EWktaUujltpvAc1lo7lg4C6nByefJB9Xqk22N/vpqOsWr1NbAntT42Qj/HF9BVWR +osvN3yMnKYWYe6oSTVnNBDM5obWAIHd3I9gcxTOTb1KsEwt2RrDs5EpB5ptS3Fjo +JfBRhNZQ3cXttrIIhsHgDn9BDNg865/xpIgktKj0gEd60Abx0PqkAIm6IZTh4Efg +7uZwfzxB+saOcddbrW2gNdzVZMC0s2Ye3sqHhtLbAJ3BlXYTxE4CAvTg54Ny+5hF +IjvjlOKgXceSG1cSfk21/wyp9RY3Ft0AEYvvp0kZScWZaoA2aSFDUrchXVhgrEbn +lJ7UptjefwRFIreAlwbKSbIDDNWnyzvIWyHfQ2aYqgnb7W7XqNPSgH9cALCfzirI +dlRHjha0bMUtrjPCC/YfMXzJBVniy0gG6Pd5uC7vz/Awn6/6HRQVNaTQASphPBQ7 +bJuz+JTfzI9OUVCMRMdnb6b35U4P9tibFmnPvzTIPe+3WUmf8aRsLS3NN3G1Webd +PMYVZpMycPaAI0Ht87axhsOzlxCWHYWjdHa+WoNNc1J90TxLCmAHquh5BDaWvjMK +0DySftJZjV7Tf1p2KosmU83LRl39B5NHMbZb1xOEZl9IWwhT/PVKTVZ25xdxWLfb +hF4l8rfvKehIp5r4t8zW1bvI2Hl6vrUvmcUVWt3BfKjxlgwRVD0vvwonMt1INesF +204vUBeXbDsUUicLwOyUgaFvJ3XU3dOyvL9MhOgM5OgoFRRhG+4AS8a5JCD8iLtq +-----END RSA PRIVATE KEY----- diff --git a/crates/boring/test/rsa.pem b/crates/boring/test/rsa.pem new file mode 100644 index 000000000..d8185fed6 --- /dev/null +++ b/crates/boring/test/rsa.pem @@ -0,0 +1,27 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIEowIBAAKCAQEAofgWCuLjybRlzo0tZWJjNiuSfb4p4fAkd/wWJcyQoTbji9k0 +l8W26mPddxHmfHQp+Vaw+4qPCJrcS2mJPMEzP1Pt0Bm4d4QlL+yRT+SFd2lZS+pC +gNMsD1W/YpRPEwOWvG6b32690r2jZ47soMZo9wGzjb/7OMg0LOL+bSf63kpaSHSX +ndS5z5rexMdbBYUsLA9e+KXBdQOS+UTo7WTBEMa2R2CapHg665xsmtdVMTBQY4uD +Zlxvb3qCo5ZwKh9kG4LT6/I5IhlJH7aGhyxXFvUK+DWNmoudF8NAco9/h9iaGNj8 +q2ethFkMLs91kzk2PAcDTW9gb54h4FRWyuXpoQIDAQABAoIBABKucaRpzQorw35S +bEUAVx8dYXUdZOlJcHtiWQ+dC6V8ljxAHj/PLyzTveyI5QO/xkObCyjIL303l2cf +UhPu2MFaJdjVzqACXuOrLot/eSFvxjvqVidTtAZExqFRJ9mylUVAoLvhowVWmC1O +n95fZCXxTUtxNEG1Xcc7m0rtzJKs45J+N/V9DP1edYH6USyPSWGp6wuA+KgHRnKK +Vf9GRx80JQY7nVNkL17eHoTWEwga+lwi0FEoW9Y7lDtWXYmKBWhUE+U8PGxlJf8f +40493HDw1WRQ/aSLoS4QTp3rn7gYgeHEvfJdkkf0UMhlknlo53M09EFPdadQ4TlU +bjqKc50CgYEA4BzEEOtIpmVdVEZNCqS7baC4crd0pqnRH/5IB3jw3bcxGn6QLvnE +tfdUdiYrqBdss1l58BQ3KhooKeQTa9AB0Hw/Py5PJdTJNPY8cQn7ouZ2KKDcmnPG +BY5t7yLc1QlQ5xHdwW1VhvKn+nXqhJTBgIPgtldC+KDV5z+y2XDwGUcCgYEAuQPE +fgmVtjL0Uyyx88GZFF1fOunH3+7cepKmtH4pxhtCoHqpWmT8YAmZxaewHgHAjLYs +p1ZSe7zFYHj7C6ul7TjeLQeZD/YwD66t62wDmpe/HlB+TnBA+njbglfIsRLtXlnD +zQkv5dTltRJ11BKBBypeeF6689rjcJIDEz9RWdcCgYAHAp9XcCSrn8wVkMVkKdb7 +DOX4IKjzdahm+ctDAJN4O/y7OW5FKebvUjdAIt2GuoTZ71iTG+7F0F+lP88jtjP4 +U4qe7VHoewl4MKOfXZKTe+YCS1XbNvfgwJ3Ltyl1OH9hWvu2yza7q+d5PCsDzqtm +27kxuvULVeya+TEdAB1ijQKBgQCH/3r6YrVH/uCWGy6bzV1nGNOdjKc9tmkfOJmN +54dxdixdpozCQ6U4OxZrsj3FcOhHBsqAHvX2uuYjagqvo3cOj1TRqNocX40omfCC +Mx3bD1yPPf/6TI2XECva/ggqEY2mYzmIiA5LVVmc5nrybr+lssFKneeyxN2Wq93S +0iJMdQKBgCGHewxzoa1r8ZMD0LETNrToK423K377UCYqXfg5XMclbrjPbEC3YI1Z +NqMtuhdBJqUnBi6tjKMF+34Xf0CUN8ncuXGO2CAYvO8PdyCixHX52ybaDjy1FtCE +6yUXjoKNXKvUm7MWGsAYH6f4IegOetN5NvmUMFStCSkh7ixZLkN1 +-----END RSA PRIVATE KEY----- diff --git a/crates/boring/test/rsa.pem.pub b/crates/boring/test/rsa.pem.pub new file mode 100644 index 000000000..093f2ba12 --- /dev/null +++ b/crates/boring/test/rsa.pem.pub @@ -0,0 +1,9 @@ +-----BEGIN PUBLIC KEY----- +MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAofgWCuLjybRlzo0tZWJj +NiuSfb4p4fAkd/wWJcyQoTbji9k0l8W26mPddxHmfHQp+Vaw+4qPCJrcS2mJPMEz +P1Pt0Bm4d4QlL+yRT+SFd2lZS+pCgNMsD1W/YpRPEwOWvG6b32690r2jZ47soMZo +9wGzjb/7OMg0LOL+bSf63kpaSHSXndS5z5rexMdbBYUsLA9e+KXBdQOS+UTo7WTB +EMa2R2CapHg665xsmtdVMTBQY4uDZlxvb3qCo5ZwKh9kG4LT6/I5IhlJH7aGhyxX +FvUK+DWNmoudF8NAco9/h9iaGNj8q2ethFkMLs91kzk2PAcDTW9gb54h4FRWyuXp +oQIDAQAB +-----END PUBLIC KEY----- diff --git a/crates/boringssl-src/Cargo.toml b/crates/boringssl-src/Cargo.toml new file mode 100644 index 000000000..1f000f300 --- /dev/null +++ b/crates/boringssl-src/Cargo.toml @@ -0,0 +1,17 @@ +[package] +name = "boringssl-src" +version = "0.1.0" +edition = "2021" + +[dependencies] +bindgen = "0.69.1" +# autotools = "0.2.6" +# cfg-if = "1.0.0" + +# [[bin]] +# name = "build_static" +# path = "src/main.rs" + +cmake = "0.1" + +[build-dependencies] diff --git a/crates/boringssl-src/boringssl/.gitkeep b/crates/boringssl-src/boringssl/.gitkeep new file mode 100644 index 000000000..e69de29bb diff --git a/crates/boringssl-src/patches/fix-CVE-2022-25638.patch b/crates/boringssl-src/patches/fix-CVE-2022-25638.patch new file mode 100644 index 000000000..74040cadf --- /dev/null +++ b/crates/boringssl-src/patches/fix-CVE-2022-25638.patch @@ -0,0 +1,190 @@ +From 5e8ee3093c09269f58ba4c77e8cbf59d24e3dec6 Mon Sep 17 00:00:00 2001 +From: David Garske +Date: Tue, 1 Feb 2022 11:28:25 -0800 +Subject: [PATCH 1/2] Fix for mutual authentication to prevent mismatch of + certificate and sig algo. Work from Sean P. ZD 13571 + +--- + src/tls13.c | 53 ++++++++++++++++++++++++++--------------------------- + 1 file changed, 26 insertions(+), 27 deletions(-) + +diff --git a/src/tls13.c b/src/tls13.c +index 7bd68905c..b41ec6980 100644 +--- a/src/tls13.c ++++ b/src/tls13.c +@@ -6536,6 +6536,8 @@ static int DoTls13CertificateVerify(WOLFSSL* ssl, byte* input, + + case TLS_ASYNC_BUILD: + { ++ int validSigAlgo = 0; ++ + /* Signature algorithm. */ + if ((args->idx - args->begin) + ENUM_LEN + ENUM_LEN > totalSz) { + ERROR_OUT(BUFFER_ERROR, exit_dcv); +@@ -6561,53 +6563,50 @@ static int DoTls13CertificateVerify(WOLFSSL* ssl, byte* input, + + /* Check for public key of required type. */ + #ifdef HAVE_ED25519 +- if (args->sigAlgo == ed25519_sa_algo && +- !ssl->peerEd25519KeyPresent) { +- WOLFSSL_MSG("Peer sent ED25519 sig but not ED25519 cert"); +- ret = SIG_VERIFY_E; +- goto exit_dcv; ++ if (args->sigAlgo == ed25519_sa_algo) { ++ WOLFSSL_MSG("Peer sent ED25519 sig"); ++ validSigAlgo = ssl->peerEd25519KeyPresent; + } + #endif + #ifdef HAVE_ED448 +- if (args->sigAlgo == ed448_sa_algo && !ssl->peerEd448KeyPresent) { +- WOLFSSL_MSG("Peer sent ED448 sig but not ED448 cert"); +- ret = SIG_VERIFY_E; +- goto exit_dcv; ++ if (args->sigAlgo == ed448_sa_algo) { ++ WOLFSSL_MSG("Peer sent ED448 sig"); ++ validSigAlgo = ssl->peerEd448KeyPresent; + } + #endif + #ifdef HAVE_ECC +- if (args->sigAlgo == ecc_dsa_sa_algo && +- !ssl->peerEccDsaKeyPresent) { +- WOLFSSL_MSG("Peer sent ECC sig but not ECC cert"); +- ret = SIG_VERIFY_E; +- goto exit_dcv; ++ if (args->sigAlgo == ecc_dsa_sa_algo) { ++ WOLFSSL_MSG("Peer sent ECC sig"); ++ validSigAlgo = ssl->peerEccDsaKeyPresent; + } + #endif + #ifdef HAVE_PQC +- if (args->sigAlgo == falcon_level1_sa_algo && !ssl->peerFalconKeyPresent) { +- WOLFSSL_MSG("Peer sent Falcon Level 1 sig but different cert"); +- ret = SIG_VERIFY_E; +- goto exit_dcv; ++ if (args->sigAlgo == falcon_level1_sa_algo) { ++ WOLFSSL_MSG("Peer sent Falcon Level 1 sig"); ++ validSigAlgo = ssl->peerFalconKeyPresent; + } +- if (args->sigAlgo == falcon_level5_sa_algo && !ssl->peerFalconKeyPresent) { +- WOLFSSL_MSG("Peer sent Falcon Level 5 sig but different cert"); +- ret = SIG_VERIFY_E; +- goto exit_dcv; ++ if (args->sigAlgo == falcon_level5_sa_algo) { ++ WOLFSSL_MSG("Peer sent Falcon Level 5 sig"); ++ validSigAlgo = ssl->peerFalconKeyPresent; + } + #endif + + #ifndef NO_RSA + if (args->sigAlgo == rsa_sa_algo) { +- WOLFSSL_MSG("Peer sent PKCS#1.5 algo but not in certificate"); ++ WOLFSSL_MSG("Peer sent PKCS#1.5 algo - not valid TLS 1.3"); + ERROR_OUT(INVALID_PARAMETER, exit_dcv); + } +- if (args->sigAlgo == rsa_pss_sa_algo && +- (ssl->peerRsaKey == NULL || !ssl->peerRsaKeyPresent)) { +- WOLFSSL_MSG("Peer sent RSA sig but not RSA cert"); ++ if (args->sigAlgo == rsa_pss_sa_algo) { ++ WOLFSSL_MSG("Peer sent RSA sig"); ++ validSigAlgo = (ssl->peerRsaKey != NULL) && ++ ssl->peerRsaKeyPresent; ++ } ++ #endif ++ if (!validSigAlgo) { ++ WOLFSSL_MSG("Sig algo doesn't correspond to certficate"); + ret = SIG_VERIFY_E; + goto exit_dcv; + } +- #endif + + sig->buffer = (byte*)XMALLOC(args->sz, ssl->heap, + DYNAMIC_TYPE_SIGNATURE); +-- +2.34.1 + + +From 139416c3dcb3131474df9d82d3517faca9d04e1b Mon Sep 17 00:00:00 2001 +From: David Garske +Date: Fri, 4 Feb 2022 10:57:35 -0800 +Subject: [PATCH 2/2] Add checking to make sure key is present in all cases. + Explicitly set `validSigAlgo` to zero with comment to clarify the default + assumption. + +--- + src/tls13.c | 23 ++++++++++++++--------- + 1 file changed, 14 insertions(+), 9 deletions(-) + +diff --git a/src/tls13.c b/src/tls13.c +index b41ec6980..ce18c35ec 100644 +--- a/src/tls13.c ++++ b/src/tls13.c +@@ -6536,7 +6536,7 @@ static int DoTls13CertificateVerify(WOLFSSL* ssl, byte* input, + + case TLS_ASYNC_BUILD: + { +- int validSigAlgo = 0; ++ int validSigAlgo; + + /* Signature algorithm. */ + if ((args->idx - args->begin) + ENUM_LEN + ENUM_LEN > totalSz) { +@@ -6562,35 +6562,41 @@ static int DoTls13CertificateVerify(WOLFSSL* ssl, byte* input, + } + + /* Check for public key of required type. */ ++ /* Assume invalid unless signature algo matches the key provided */ ++ validSigAlgo = 0; + #ifdef HAVE_ED25519 + if (args->sigAlgo == ed25519_sa_algo) { + WOLFSSL_MSG("Peer sent ED25519 sig"); +- validSigAlgo = ssl->peerEd25519KeyPresent; ++ validSigAlgo = (ssl->peerEd25519Key != NULL) && ++ ssl->peerEd25519KeyPresent; + } + #endif + #ifdef HAVE_ED448 + if (args->sigAlgo == ed448_sa_algo) { + WOLFSSL_MSG("Peer sent ED448 sig"); +- validSigAlgo = ssl->peerEd448KeyPresent; ++ validSigAlgo = (ssl->peerEd448Key != NULL) && ++ ssl->peerEd448KeyPresent; + } + #endif + #ifdef HAVE_ECC + if (args->sigAlgo == ecc_dsa_sa_algo) { + WOLFSSL_MSG("Peer sent ECC sig"); +- validSigAlgo = ssl->peerEccDsaKeyPresent; ++ validSigAlgo = (ssl->peerEccDsaKey != NULL) && ++ ssl->peerEccDsaKeyPresent; + } + #endif + #ifdef HAVE_PQC + if (args->sigAlgo == falcon_level1_sa_algo) { + WOLFSSL_MSG("Peer sent Falcon Level 1 sig"); +- validSigAlgo = ssl->peerFalconKeyPresent; ++ validSigAlgo = (ssl->peerFalconKey != NULL) && ++ ssl->peerFalconKeyPresent; + } + if (args->sigAlgo == falcon_level5_sa_algo) { + WOLFSSL_MSG("Peer sent Falcon Level 5 sig"); +- validSigAlgo = ssl->peerFalconKeyPresent; ++ validSigAlgo = (ssl->peerFalconKey != NULL) && ++ ssl->peerFalconKeyPresent; + } + #endif +- + #ifndef NO_RSA + if (args->sigAlgo == rsa_sa_algo) { + WOLFSSL_MSG("Peer sent PKCS#1.5 algo - not valid TLS 1.3"); +@@ -6604,8 +6610,7 @@ static int DoTls13CertificateVerify(WOLFSSL* ssl, byte* input, + #endif + if (!validSigAlgo) { + WOLFSSL_MSG("Sig algo doesn't correspond to certficate"); +- ret = SIG_VERIFY_E; +- goto exit_dcv; ++ ERROR_OUT(SIG_VERIFY_E, exit_dcv); + } + + sig->buffer = (byte*)XMALLOC(args->sz, ssl->heap, +-- +2.34.1 + diff --git a/crates/boringssl-src/patches/fix-CVE-2022-25640.patch b/crates/boringssl-src/patches/fix-CVE-2022-25640.patch new file mode 100644 index 000000000..7b9739efc --- /dev/null +++ b/crates/boringssl-src/patches/fix-CVE-2022-25640.patch @@ -0,0 +1,126 @@ +From f3ff5e38cbd65a029500024853c5a76f6d0c270a Mon Sep 17 00:00:00 2001 +From: David Garske +Date: Fri, 4 Feb 2022 16:36:21 -0800 +Subject: [PATCH 1/2] Improve the client certificate checking logic. Make sure + calling `wolfSSL_CTX_mutual_auth` is also checked. + +--- + src/internal.c | 7 ++++--- + src/tls13.c | 12 ++++++++++++ + wolfssl/internal.h | 4 +++- + 3 files changed, 19 insertions(+), 4 deletions(-) + +diff --git a/src/internal.c b/src/internal.c +index 16b37dc9d..4965662aa 100644 +--- a/src/internal.c ++++ b/src/internal.c +@@ -11136,7 +11136,7 @@ int InitSigPkCb(WOLFSSL* ssl, SignatureCtx* sigCtx) + + + #if !defined(NO_WOLFSSL_CLIENT) || !defined(WOLFSSL_NO_CLIENT_AUTH) +-static void DoCertFatalAlert(WOLFSSL* ssl, int ret) ++void DoCertFatalAlert(WOLFSSL* ssl, int ret) + { + int alertWhy; + if (ssl == NULL || ret == 0) { +@@ -31595,7 +31595,8 @@ static int DefTicketEncCb(WOLFSSL* ssl, byte key_name[WOLFSSL_TICKET_NAME_SZ], + } + + #if !defined(NO_CERTS) && !defined(WOLFSSL_NO_CLIENT_AUTH) +- if (ssl->options.verifyPeer && ssl->options.failNoCert) { ++ if (ssl->options.verifyPeer && ++ (ssl->options.mutualAuth || ssl->options.failNoCert)) { + if (!ssl->options.havePeerCert) { + WOLFSSL_MSG("client didn't present peer cert"); + ERROR_OUT(NO_PEER_CERT, exit_dcke); +@@ -31606,7 +31607,7 @@ static int DefTicketEncCb(WOLFSSL* ssl, byte key_name[WOLFSSL_TICKET_NAME_SZ], + if (!ssl->options.havePeerCert && + !ssl->options.usingPSK_cipher) { + WOLFSSL_MSG("client didn't present peer cert"); +- return NO_PEER_CERT; ++ ERROR_OUT(NO_PEER_CERT, exit_dcke); + } + } + #endif /* !NO_CERTS && !WOLFSSL_NO_CLIENT_AUTH */ +diff --git a/src/tls13.c b/src/tls13.c +index 7bd68905c..0b95458c7 100644 +--- a/src/tls13.c ++++ b/src/tls13.c +@@ -6884,6 +6884,18 @@ int DoTls13Finished(WOLFSSL* ssl, const byte* input, word32* inOutIdx, + WOLFSSL_START(WC_FUNC_FINISHED_DO); + WOLFSSL_ENTER("DoTls13Finished"); + ++#if !defined(NO_CERTS) && !defined(WOLFSSL_NO_CLIENT_AUTH) ++ /* verify the client sent certificate if required */ ++ if (ssl->options.side == WOLFSSL_SERVER_END && ++ (ssl->options.mutualAuth || ssl->options.failNoCert)) { ++ if (!ssl->options.havePeerVerify && !ssl->options.resuming) { ++ ret = NO_PEER_CERT; ++ DoCertFatalAlert(ssl, ret); ++ return ret; ++ } ++ } ++#endif ++ + /* check against totalSz */ + if (*inOutIdx + size + ssl->keys.padSz > totalSz) + return BUFFER_E; +diff --git a/wolfssl/internal.h b/wolfssl/internal.h +index ec3ea5502..30c5363d4 100644 +--- a/wolfssl/internal.h ++++ b/wolfssl/internal.h +@@ -4882,7 +4882,9 @@ WOLFSSL_LOCAL int BuildTlsFinished(WOLFSSL* ssl, Hashes* hashes, + WOLFSSL_LOCAL void FreeArrays(WOLFSSL* ssl, int keep); + WOLFSSL_LOCAL int CheckAvailableSize(WOLFSSL *ssl, int size); + WOLFSSL_LOCAL int GrowInputBuffer(WOLFSSL* ssl, int size, int usedLength); +- ++#if !defined(NO_WOLFSSL_CLIENT) || !defined(WOLFSSL_NO_CLIENT_AUTH) ++WOLFSSL_LOCAL void DoCertFatalAlert(WOLFSSL* ssl, int ret); ++#endif + #ifndef NO_TLS + WOLFSSL_LOCAL int MakeTlsMasterSecret(WOLFSSL*); + #ifndef WOLFSSL_AEAD_ONLY +-- +2.34.1 + + +From ed49457e2ed0d71537fec588a28047a6994a7a2b Mon Sep 17 00:00:00 2001 +From: David Garske +Date: Mon, 7 Feb 2022 16:26:18 -0800 +Subject: [PATCH 2/2] Include the `havePeerCert` argument in the check. + +--- + src/tls13.c | 9 ++++++--- + 1 file changed, 6 insertions(+), 3 deletions(-) + +diff --git a/src/tls13.c b/src/tls13.c +index 0b95458c7..259ad8a1c 100644 +--- a/src/tls13.c ++++ b/src/tls13.c +@@ -71,6 +71,8 @@ + * You cannot use wc_psk_client_cs_callback type callback on client. + * WOLFSSL_CHECK_ALERT_ON_ERR + * Check for alerts during the handshake in the event of an error. ++ * WOLFSSL_NO_CLIENT_CERT_ERROR ++ * Requires client to set a client certificate + */ + + #ifdef HAVE_CONFIG_H +@@ -6886,10 +6888,11 @@ int DoTls13Finished(WOLFSSL* ssl, const byte* input, word32* inOutIdx, + + #if !defined(NO_CERTS) && !defined(WOLFSSL_NO_CLIENT_AUTH) + /* verify the client sent certificate if required */ +- if (ssl->options.side == WOLFSSL_SERVER_END && ++ if (ssl->options.side == WOLFSSL_SERVER_END && !ssl->options.resuming && + (ssl->options.mutualAuth || ssl->options.failNoCert)) { +- if (!ssl->options.havePeerVerify && !ssl->options.resuming) { +- ret = NO_PEER_CERT; ++ if (!ssl->options.havePeerCert || !ssl->options.havePeerVerify) { ++ ret = NO_PEER_CERT; /* NO_PEER_VERIFY */ ++ WOLFSSL_MSG("TLS v1.3 client did not present peer cert"); + DoCertFatalAlert(ssl, ret); + return ret; + } +-- +2.34.1 + diff --git a/crates/boringssl-src/patches/fix-CVE-2022-39173.patch b/crates/boringssl-src/patches/fix-CVE-2022-39173.patch new file mode 100644 index 000000000..9c1467c78 --- /dev/null +++ b/crates/boringssl-src/patches/fix-CVE-2022-39173.patch @@ -0,0 +1,62 @@ +From 2aabe592c4f8c64deb9457e4f8e74b7f60ebaf86 Mon Sep 17 00:00:00 2001 +From: Maximilian Ammann +Date: Wed, 19 Oct 2022 13:29:09 +0200 +Subject: [PATCH] Fix CVE-2022-39173 + +--- + src/tls13.c | 34 ++++++++++++++++++++++++++++------ + 1 file changed, 28 insertions(+), 6 deletions(-) + +diff --git a/src/tls13.c b/src/tls13.c +index 7bd68905c..7bab7142f 100644 +--- a/src/tls13.c ++++ b/src/tls13.c +@@ -3834,17 +3834,39 @@ static void RefineSuites(WOLFSSL* ssl, Suites* peerSuites) + { + byte suites[WOLFSSL_MAX_SUITE_SZ]; + word16 suiteSz = 0; +- word16 i, j; ++ word16 i; ++ word16 j; + + XMEMSET(suites, 0, WOLFSSL_MAX_SUITE_SZ); + +- for (i = 0; i < ssl->suites->suiteSz; i += 2) { ++ if (!ssl->options.useClientOrder) { ++ /* Server order refining. */ ++ for (i = 0; i < ssl->suites->suiteSz; i += 2) { ++ for (j = 0; j < peerSuites->suiteSz; j += 2) { ++ if ((ssl->suites->suites[i+0] == peerSuites->suites[j+0]) && ++ (ssl->suites->suites[i+1] == peerSuites->suites[j+1])) { ++ suites[suiteSz++] = peerSuites->suites[j+0]; ++ suites[suiteSz++] = peerSuites->suites[j+1]; ++ break; ++ } ++ } ++ if (suiteSz == WOLFSSL_MAX_SUITE_SZ) ++ break; ++ } ++ } ++ else { ++ /* Client order refining. */ + for (j = 0; j < peerSuites->suiteSz; j += 2) { +- if (ssl->suites->suites[i+0] == peerSuites->suites[j+0] && +- ssl->suites->suites[i+1] == peerSuites->suites[j+1]) { +- suites[suiteSz++] = peerSuites->suites[j+0]; +- suites[suiteSz++] = peerSuites->suites[j+1]; ++ for (i = 0; i < ssl->suites->suiteSz; i += 2) { ++ if ((ssl->suites->suites[i+0] == peerSuites->suites[j+0]) && ++ (ssl->suites->suites[i+1] == peerSuites->suites[j+1])) { ++ suites[suiteSz++] = peerSuites->suites[j+0]; ++ suites[suiteSz++] = peerSuites->suites[j+1]; ++ break; ++ } + } ++ if (suiteSz == WOLFSSL_MAX_SUITE_SZ) ++ break; + } + } + +-- +2.34.1 + diff --git a/crates/boringssl-src/patches/fix-CVE-2022-42905.patch b/crates/boringssl-src/patches/fix-CVE-2022-42905.patch new file mode 100644 index 000000000..377d947bf --- /dev/null +++ b/crates/boringssl-src/patches/fix-CVE-2022-42905.patch @@ -0,0 +1,462 @@ +From f19e7daf5b60e253042600424c18d008c526d828 Mon Sep 17 00:00:00 2001 +From: JacobBarthelmeh +Date: Tue, 11 Oct 2022 13:14:59 -0700 +Subject: [PATCH] additional sanity checks on debug callback + +--- + examples/client/client.c | 4 ++ + examples/server/server.c | 7 ++++ + src/internal.c | 84 +++++++++++++++++++++++----------------- + src/tls13.c | 35 +++++++++-------- + wolfssl/internal.h | 3 +- + wolfssl/test.h | 20 ++++++++++ + 6 files changed, 100 insertions(+), 53 deletions(-) + +diff --git a/examples/client/client.c b/examples/client/client.c +index 6e1660472..e7ca8de62 100644 +--- a/examples/client/client.c ++++ b/examples/client/client.c +@@ -1843,6 +1843,7 @@ static int client_srtp_test(WOLFSSL *ssl, func_args *args) + } + #endif /* WOLFSSL_SRTP */ + ++ + THREAD_RETURN WOLFSSL_THREAD client_test(void* args) + { + SOCKET_T sockfd = WOLFSSL_SOCKET_INVALID; +@@ -2930,6 +2931,9 @@ THREAD_RETURN WOLFSSL_THREAD client_test(void* args) + ctx = wolfSSL_CTX_new_ex(method(heap), heap); + if (ctx == NULL) + err_sys("unable to get ctx"); ++#ifdef WOLFSSL_CALLBACKS ++ wolfSSL_CTX_set_msg_callback(ctx, msgDebugCb); ++#endif + + if (wolfSSL_CTX_load_static_memory(&ctx, NULL, memoryIO, sizeof(memoryIO), + WOLFMEM_IO_POOL_FIXED | WOLFMEM_TRACK_STATS, 1) != WOLFSSL_SUCCESS) { +diff --git a/examples/server/server.c b/examples/server/server.c +index f3ff44469..ee3ed7aa6 100644 +--- a/examples/server/server.c ++++ b/examples/server/server.c +@@ -1368,6 +1368,7 @@ static int server_srtp_test(WOLFSSL *ssl, func_args *args) + } + #endif + ++ + THREAD_RETURN WOLFSSL_THREAD server_test(void* args) + { + SOCKET_T sockfd = WOLFSSL_SOCKET_INVALID; +@@ -2336,6 +2337,9 @@ THREAD_RETURN WOLFSSL_THREAD server_test(void* args) + if (method != NULL) { + ctx = SSL_CTX_new(method(NULL)); + } ++#ifdef WOLFSSL_CALLBACKS ++ wolfSSL_CTX_set_msg_callback(ctx, msgDebugCb); ++#endif + #endif /* WOLFSSL_STATIC_MEMORY */ + if (ctx == NULL) + err_sys_ex(catastrophic, "unable to get ctx"); +@@ -3581,6 +3585,9 @@ exit: + #ifdef HAVE_SECURE_RENEGOTIATION + (void) forceScr; + #endif ++#if defined(WOLFSSL_CALLBACKS) && defined(WOLFSSL_EARLY_DATA) ++ (void) earlyData; ++#endif + #ifndef WOLFSSL_TIRTOS + return 0; + #endif +diff --git a/src/internal.c b/src/internal.c +index 930769152..7d5f20f80 100644 +--- a/src/internal.c ++++ b/src/internal.c +@@ -9208,7 +9208,7 @@ static int SendHandshakeMsg(WOLFSSL* ssl, byte* input, word32 inputSz, + } + if (ssl->toInfoOn) { + AddPacketInfo(ssl, packetName, handshake, +- output, outputSz, WRITE_PROTO, ssl->heap); ++ output, outputSz, WRITE_PROTO, 0, ssl->heap); + } + #endif + ssl->fragOffset += fragSz; +@@ -14739,11 +14739,12 @@ static int DoHandShakeMsgType(WOLFSSL* ssl, byte* input, word32* inOutIdx, + } + + #if defined(WOLFSSL_CALLBACKS) || defined(OPENSSL_EXTRA) +- /* add name later, add on record and handshake header part back on */ ++ /* add name later, add the handshake header part back on and record layer ++ * header */ + if (ssl->toInfoOn) { +- int add = RECORD_HEADER_SZ + HANDSHAKE_HEADER_SZ; +- AddPacketInfo(ssl, 0, handshake, input + *inOutIdx - add, +- size + add, READ_PROTO, ssl->heap); ++ AddPacketInfo(ssl, 0, handshake, input + *inOutIdx - ++ HANDSHAKE_HEADER_SZ, size + HANDSHAKE_HEADER_SZ, READ_PROTO, ++ RECORD_HEADER_SZ, ssl->heap); + #ifdef WOLFSSL_CALLBACKS + AddLateRecordHeader(&ssl->curRL, &ssl->timeoutInfo); + #endif +@@ -17792,11 +17793,14 @@ static int DoAlert(WOLFSSL* ssl, byte* input, word32* inOutIdx, int* type) + #if defined(WOLFSSL_CALLBACKS) || defined(OPENSSL_EXTRA) + if (ssl->hsInfoOn) + AddPacketName(ssl, "Alert"); +- if (ssl->toInfoOn) ++ if (ssl->toInfoOn) { + /* add record header back on to info + alert bytes level/code */ +- AddPacketInfo(ssl, "Alert", alert, input + *inOutIdx - +- RECORD_HEADER_SZ, RECORD_HEADER_SZ + ALERT_SIZE, +- READ_PROTO, ssl->heap); ++ AddPacketInfo(ssl, "Alert", alert, input + *inOutIdx, ALERT_SIZE, ++ READ_PROTO, RECORD_HEADER_SZ, ssl->heap); ++ #ifdef WOLFSSL_CALLBACKS ++ AddLateRecordHeader(&ssl->curRL, &ssl->timeoutInfo); ++ #endif ++ } + #endif + + if (IsEncryptionOn(ssl, 0)) { +@@ -18815,9 +18819,8 @@ int ProcessReplyEx(WOLFSSL* ssl, int allowSocketErr) + AddPacketInfo(ssl, "ChangeCipher", + change_cipher_spec, + ssl->buffers.inputBuffer.buffer + +- ssl->buffers.inputBuffer.idx - RECORD_HEADER_SZ - +- (ssl->options.dtls ? DTLS_RECORD_EXTRA : 0), +- 1 + RECORD_HEADER_SZ, READ_PROTO, ssl->heap); ++ ssl->buffers.inputBuffer.idx, ++ 1, READ_PROTO, RECORD_HEADER_SZ, ssl->heap); + #ifdef WOLFSSL_CALLBACKS + AddLateRecordHeader(&ssl->curRL, &ssl->timeoutInfo); + #endif +@@ -19190,7 +19193,7 @@ int SendChangeCipher(WOLFSSL* ssl) + if (ssl->hsInfoOn) AddPacketName(ssl, "ChangeCipher"); + if (ssl->toInfoOn) + AddPacketInfo(ssl, "ChangeCipher", change_cipher_spec, output, +- sendSz, WRITE_PROTO, ssl->heap); ++ sendSz, WRITE_PROTO, 0, ssl->heap); + #endif + ssl->buffers.outputBuffer.length += sendSz; + +@@ -20106,7 +20109,7 @@ int SendFinished(WOLFSSL* ssl) + if (ssl->hsInfoOn) AddPacketName(ssl, "Finished"); + if (ssl->toInfoOn) + AddPacketInfo(ssl, "Finished", handshake, output, sendSz, +- WRITE_PROTO, ssl->heap); ++ WRITE_PROTO, 0, ssl->heap); + #endif + + ssl->buffers.outputBuffer.length += sendSz; +@@ -20548,7 +20551,7 @@ int SendCertificate(WOLFSSL* ssl) + AddPacketName(ssl, "Certificate"); + if (ssl->toInfoOn) + AddPacketInfo(ssl, "Certificate", handshake, output, sendSz, +- WRITE_PROTO, ssl->heap); ++ WRITE_PROTO, 0, ssl->heap); + #endif + + ssl->buffers.outputBuffer.length += sendSz; +@@ -20744,7 +20747,7 @@ int SendCertificateRequest(WOLFSSL* ssl) + AddPacketName(ssl, "CertificateRequest"); + if (ssl->toInfoOn) + AddPacketInfo(ssl, "CertificateRequest", handshake, output, sendSz, +- WRITE_PROTO, ssl->heap); ++ WRITE_PROTO, 0, ssl->heap); + #endif + ssl->buffers.outputBuffer.length += sendSz; + if (ssl->options.groupMessages) +@@ -20858,7 +20861,7 @@ static int BuildCertificateStatus(WOLFSSL* ssl, byte type, buffer* status, + AddPacketName(ssl, "CertificateStatus"); + if (ret == 0 && ssl->toInfoOn) + AddPacketInfo(ssl, "CertificateStatus", handshake, output, sendSz, +- WRITE_PROTO, ssl->heap); ++ WRITE_PROTO, 0, ssl->heap); + #endif + + if (ret == 0) { +@@ -21705,7 +21708,7 @@ static int SendAlert_ex(WOLFSSL* ssl, int severity, int type) + if (ssl->hsInfoOn) + AddPacketName(ssl, "Alert"); + if (ssl->toInfoOn) +- AddPacketInfo(ssl, "Alert", alert, output, sendSz, WRITE_PROTO, ++ AddPacketInfo(ssl, "Alert", alert, output, sendSz, WRITE_PROTO, 0, + ssl->heap); + #endif + +@@ -23785,17 +23788,22 @@ int PickHashSigAlgo(WOLFSSL* ssl, const byte* hashSigAlgo, word32 hashSigAlgoSz) + * type type of packet being sent + * data data bing sent with packet + * sz size of data buffer ++ * lateRL save space for record layer in TimoutInfo struct + * written 1 if this packet is being written to wire, 0 if being read + * heap custom heap to use for mallocs/frees + */ + void AddPacketInfo(WOLFSSL* ssl, const char* name, int type, +- const byte* data, int sz, int written, void* heap) ++ const byte* data, int sz, int written, int lateRL, void* heap) + { + #ifdef WOLFSSL_CALLBACKS + TimeoutInfo* info = &ssl->timeoutInfo; + + if (info->numberPackets < (MAX_PACKETS_HANDSHAKE - 1)) { + WOLFSSL_TIMEVAL currTime; ++ int totalSz; ++ ++ /* add in space for post record layer */ ++ totalSz = sz + lateRL; + + /* may add name after */ + if (name) { +@@ -23805,18 +23813,24 @@ int PickHashSigAlgo(WOLFSSL* ssl, const byte* hashSigAlgo, word32 hashSigAlgoSz) + } + + /* add data, put in buffer if bigger than static buffer */ +- info->packets[info->numberPackets].valueSz = sz; +- if (sz < MAX_VALUE_SZ) +- XMEMCPY(info->packets[info->numberPackets].value, data, sz); ++ info->packets[info->numberPackets].valueSz = totalSz; ++ if (totalSz < MAX_VALUE_SZ) { ++ XMEMCPY(info->packets[info->numberPackets].value, data + lateRL, ++ sz); ++ } + else { + info->packets[info->numberPackets].bufferValue = +- (byte*)XMALLOC(sz, heap, DYNAMIC_TYPE_INFO); +- if (!info->packets[info->numberPackets].bufferValue) ++ (byte*)XMALLOC(totalSz, heap, DYNAMIC_TYPE_INFO); ++ if (!info->packets[info->numberPackets].bufferValue) { + /* let next alloc catch, just don't fill, not fatal here */ + info->packets[info->numberPackets].valueSz = 0; +- else +- XMEMCPY(info->packets[info->numberPackets].bufferValue, +- data, sz); ++ } ++ else { ++ /* copy over data (which has the handshake header), leaving ++ * room for post record layer header if set */ ++ XMEMCPY(info->packets[info->numberPackets].bufferValue + ++ lateRL, data, sz); ++ } + } + gettimeofday(&currTime, 0); + info->packets[info->numberPackets].timestamp.tv_sec = +@@ -23827,7 +23841,7 @@ int PickHashSigAlgo(WOLFSSL* ssl, const byte* hashSigAlgo, word32 hashSigAlgoSz) + } + #endif /* WOLFSSL_CALLBACKS */ + #ifdef OPENSSL_EXTRA +- if (ssl->protoMsgCb != NULL && sz > RECORD_HEADER_SZ) { ++ if ((ssl->protoMsgCb != NULL) && (sz > 0)) { + /* version from hex to dec 16 is 16^1, 256 from 16^2 and + 4096 from 16^3 */ + int version = (ssl->version.minor & 0x0F) + +@@ -23836,8 +23850,7 @@ int PickHashSigAlgo(WOLFSSL* ssl, const byte* hashSigAlgo, word32 hashSigAlgoSz) + ((ssl->version.major & 0xF0) << 12); + + ssl->protoMsgCb(written, version, type, +- (const void *)(data + RECORD_HEADER_SZ), +- (size_t)(sz - RECORD_HEADER_SZ), ++ (const void *)data, (size_t)sz, + ssl, ssl->protoMsgCtx); + } + #endif /* OPENSSL_EXTRA */ +@@ -23846,6 +23859,7 @@ int PickHashSigAlgo(WOLFSSL* ssl, const byte* hashSigAlgo, word32 hashSigAlgoSz) + (void)heap; + (void)type; + (void)ssl; ++ (void)lateRL; + } + + #endif /* WOLFSSL_CALLBACKS */ +@@ -24655,7 +24669,7 @@ static int HashSkeData(WOLFSSL* ssl, enum wc_HashType hashType, + if (ssl->hsInfoOn) AddPacketName(ssl, "ClientHello"); + if (ssl->toInfoOn) + AddPacketInfo(ssl, "ClientHello", handshake, output, sendSz, +- WRITE_PROTO, ssl->heap); ++ WRITE_PROTO, 0, ssl->heap); + #endif + + ssl->options.buildingMsg = 0; +@@ -27964,7 +27978,7 @@ int SendClientKeyExchange(WOLFSSL* ssl) + AddPacketName(ssl, "ClientKeyExchange"); + if (ssl->toInfoOn) + AddPacketInfo(ssl, "ClientKeyExchange", handshake, +- args->output, args->sendSz, WRITE_PROTO, ssl->heap); ++ args->output, args->sendSz, WRITE_PROTO, 0, ssl->heap); + #endif + + ssl->buffers.outputBuffer.length += args->sendSz; +@@ -28973,7 +28987,7 @@ static int DoSessionTicket(WOLFSSL* ssl, const byte* input, word32* inOutIdx, + AddPacketName(ssl, "ServerHello"); + if (ssl->toInfoOn) + AddPacketInfo(ssl, "ServerHello", handshake, output, sendSz, +- WRITE_PROTO, ssl->heap); ++ WRITE_PROTO, 0, ssl->heap); + #endif + + ssl->options.serverState = SERVER_HELLO_COMPLETE; +@@ -32296,7 +32310,7 @@ static int DoSessionTicket(WOLFSSL* ssl, const byte* input, word32* inOutIdx, + AddPacketName(ssl, "ServerHelloDone"); + if (ssl->toInfoOn) + AddPacketInfo(ssl, "ServerHelloDone", handshake, output, sendSz, +- WRITE_PROTO, ssl->heap); ++ WRITE_PROTO, 0, ssl->heap); + #endif + ssl->options.serverState = SERVER_HELLODONE_COMPLETE; + ssl->options.buildingMsg = 0; +@@ -33424,7 +33438,7 @@ static int DefTicketEncCb(WOLFSSL* ssl, byte key_name[WOLFSSL_TICKET_NAME_SZ], + AddPacketName(ssl, "HelloVerifyRequest"); + if (ssl->toInfoOn) + AddPacketInfo(ssl, "HelloVerifyRequest", handshake, output, +- sendSz, WRITE_PROTO, ssl->heap); ++ sendSz, WRITE_PROTO, 0, ssl->heap); + #endif + + /* are we in scr */ +diff --git a/src/tls13.c b/src/tls13.c +index dec6d88c4..655934e4e 100644 +--- a/src/tls13.c ++++ b/src/tls13.c +@@ -3609,7 +3609,7 @@ int SendTls13ClientHello(WOLFSSL* ssl) + if (ssl->hsInfoOn) AddPacketName(ssl, "ClientHello"); + if (ssl->toInfoOn) { + AddPacketInfo(ssl, "ClientHello", handshake, args->output, args->sendSz, +- WRITE_PROTO, ssl->heap); ++ WRITE_PROTO, 0, ssl->heap); + } + #endif + +@@ -5717,7 +5717,7 @@ int SendTls13ServerHello(WOLFSSL* ssl, byte extMsgType) + AddPacketName(ssl, "ServerHello"); + if (ssl->toInfoOn) { + AddPacketInfo(ssl, "ServerHello", handshake, output, sendSz, +- WRITE_PROTO, ssl->heap); ++ WRITE_PROTO, 0, ssl->heap); + } + #endif + +@@ -5852,7 +5852,7 @@ static int SendTls13EncryptedExtensions(WOLFSSL* ssl) + AddPacketName(ssl, "EncryptedExtensions"); + if (ssl->toInfoOn) { + AddPacketInfo(ssl, "EncryptedExtensions", handshake, output, +- sendSz, WRITE_PROTO, ssl->heap); ++ sendSz, WRITE_PROTO, 0, ssl->heap); + } + #endif + +@@ -5988,7 +5988,7 @@ static int SendTls13CertificateRequest(WOLFSSL* ssl, byte* reqCtx, + AddPacketName(ssl, "CertificateRequest"); + if (ssl->toInfoOn) { + AddPacketInfo(ssl, "CertificateRequest", handshake, output, +- sendSz, WRITE_PROTO, ssl->heap); ++ sendSz, WRITE_PROTO, 0, ssl->heap); + } + #endif + +@@ -6734,7 +6734,7 @@ static int SendTls13Certificate(WOLFSSL* ssl) + AddPacketName(ssl, "Certificate"); + if (ssl->toInfoOn) { + AddPacketInfo(ssl, "Certificate", handshake, output, +- sendSz, WRITE_PROTO, ssl->heap); ++ sendSz, WRITE_PROTO, 0, ssl->heap); + } + #endif + +@@ -7214,7 +7214,8 @@ static int SendTls13CertificateVerify(WOLFSSL* ssl) + AddPacketName(ssl, "CertificateVerify"); + if (ssl->toInfoOn) { + AddPacketInfo(ssl, "CertificateVerify", handshake, +- args->output, args->sendSz, WRITE_PROTO, ssl->heap); ++ args->output, args->sendSz, WRITE_PROTO, 0, ++ ssl->heap); + } + #endif + +@@ -8052,7 +8053,7 @@ static int SendTls13Finished(WOLFSSL* ssl) + if (ssl->hsInfoOn) AddPacketName(ssl, "Finished"); + if (ssl->toInfoOn) { + AddPacketInfo(ssl, "Finished", handshake, output, sendSz, +- WRITE_PROTO, ssl->heap); ++ WRITE_PROTO, 0, ssl->heap); + } + #endif + +@@ -8254,13 +8255,13 @@ static int SendTls13KeyUpdate(WOLFSSL* ssl) + if (sendSz < 0) + return BUILD_MSG_ERROR; + +- #ifdef WOLFSSL_CALLBACKS +- if (ssl->hsInfoOn) AddPacketName(ssl, "KeyUpdate"); +- if (ssl->toInfoOn) { +- AddPacketInfo(ssl, "KeyUpdate", handshake, output, sendSz, +- WRITE_PROTO, ssl->heap); +- } +- #endif ++ #if defined(WOLFSSL_CALLBACKS) || defined(OPENSSL_EXTRA) ++ if (ssl->hsInfoOn) AddPacketName(ssl, "KeyUpdate"); ++ if (ssl->toInfoOn) { ++ AddPacketInfo(ssl, "KeyUpdate", handshake, output, sendSz, ++ WRITE_PROTO, 0, ssl->heap); ++ } ++ #endif + + ssl->buffers.outputBuffer.length += sendSz; + +@@ -9327,9 +9328,9 @@ int DoTls13HandShakeMsgType(WOLFSSL* ssl, byte* input, word32* inOutIdx, + #ifdef WOLFSSL_CALLBACKS + /* add name later, add on record and handshake header part back on */ + if (ssl->toInfoOn) { +- int add = RECORD_HEADER_SZ + HANDSHAKE_HEADER_SZ; +- AddPacketInfo(ssl, 0, handshake, input + *inOutIdx - add, +- size + add, READ_PROTO, ssl->heap); ++ AddPacketInfo(ssl, 0, handshake, input + *inOutIdx - ++ HANDSHAKE_HEADER_SZ, size + HANDSHAKE_HEADER_SZ, READ_PROTO, ++ RECORD_HEADER_SZ, ssl->heap); + AddLateRecordHeader(&ssl->curRL, &ssl->timeoutInfo); + } + #endif +diff --git a/wolfssl/internal.h b/wolfssl/internal.h +index d2833f72d..6465dd6f4 100644 +--- a/wolfssl/internal.h ++++ b/wolfssl/internal.h +@@ -4945,7 +4945,8 @@ WOLFSSL_API void SSL_ResourceFree(WOLFSSL* ssl); /* Micrium uses */ + WOLFSSL_LOCAL void InitTimeoutInfo(TimeoutInfo* info); + WOLFSSL_LOCAL void FreeTimeoutInfo(TimeoutInfo* info, void* heap); + WOLFSSL_LOCAL void AddPacketInfo(WOLFSSL* ssl, const char* name, int type, +- const byte* data, int sz, int written, void* heap); ++ const byte* data, int sz, int written, int lateRL, ++ void* heap); + WOLFSSL_LOCAL void AddLateName(const char* name, TimeoutInfo* info); + WOLFSSL_LOCAL void AddLateRecordHeader(const RecordLayerHeader* rl, + TimeoutInfo* info); +diff --git a/wolfssl/test.h b/wolfssl/test.h +index c0abc7304..1e77f6824 100644 +--- a/wolfssl/test.h ++++ b/wolfssl/test.h +@@ -2500,6 +2500,26 @@ static WC_INLINE unsigned int my_psk_client_cs_cb(WOLFSSL* ssl, + #endif + #endif /* USE_WINDOWS_API */ + ++#ifdef WOLFSSL_CALLBACKS ++/* only for debug use! */ ++static WC_INLINE void msgDebugCb(int write_p, int version, int content_type, ++ const void *buf, size_t len, WOLFSSL *ssl, void *arg) ++{ ++ size_t z; ++ byte* pt; ++ ++ printf("Version %02X, content type = %d\n", version, content_type); ++ printf("%s ", (write_p)? "WRITING" : "READING"); ++ pt = (byte*)buf; ++ printf("DATA [%zu]: ", len); ++ for (z = 0; z < len; z++) ++ printf("%02X", pt[z]); ++ printf("\n"); ++ ++ (void)arg; ++ (void)ssl; ++} ++#endif /* WOLFSSL_CALLBACKS */ + + #if defined(HAVE_OCSP) && defined(WOLFSSL_NONBLOCK_OCSP) + static WC_INLINE int OCSPIOCb(void* ioCtx, const char* url, int urlSz, +-- +2.34.1 + diff --git a/crates/boringssl-src/src/lib.rs b/crates/boringssl-src/src/lib.rs new file mode 100644 index 000000000..19ed0faef --- /dev/null +++ b/crates/boringssl-src/src/lib.rs @@ -0,0 +1,155 @@ +use std::{ + collections::HashSet, + io, + io::ErrorKind, + path::{Path, PathBuf}, + process::Command, +}; + +use cmake::Config; + +pub struct BoringSSLOptions { + pub asan: bool, + pub sancov: bool, + pub deterministic: bool, + + pub gcov_analysis: bool, + pub llvm_cov_analysis: bool, + + pub git_ref: String, + pub out_dir: PathBuf, + pub source_dir: PathBuf, +} + +#[derive(Debug)] +struct IgnoreMacros(HashSet); + +impl bindgen::callbacks::ParseCallbacks for IgnoreMacros { + fn will_parse_macro(&self, name: &str) -> bindgen::callbacks::MacroParsingBehavior { + if self.0.contains(name) { + bindgen::callbacks::MacroParsingBehavior::Ignore + } else { + bindgen::callbacks::MacroParsingBehavior::Default + } + } +} + +fn _patch_boringssl>( + source_dir: &PathBuf, + out_dir: P, + patch: &str, +) -> std::io::Result<()> { + let status = Command::new("git") + .current_dir(out_dir) + .arg("am") + .arg(source_dir.join("patches").join(patch).to_str().unwrap()) + .status()?; + + if !status.success() { + return Err(io::Error::from(ErrorKind::Other)); + } + + Ok(()) +} + +fn clone_boringssl>(dest: &P, options: &BoringSSLOptions) -> std::io::Result<()> { + //return Ok(()); + std::fs::remove_dir_all(dest)?; + let status = Command::new("git") + .arg("clone") + .arg("--depth") + .arg("1") + .arg("--branch") + .arg(&options.git_ref) + .arg("https://github.com/google/boringssl.git") + .arg(dest.as_ref().to_str().unwrap()) + .status()?; + + if !status.success() { + return Err(io::Error::from(ErrorKind::Other)); + } + + Ok(()) +} + +fn build_boringssl>(dest: &P, options: &BoringSSLOptions) -> PathBuf { + let mut boring_conf = Config::new(&options.source_dir); + boring_conf + .define("BUILD_SHARED_LIBS", "OFF") + .define("CMAKE_INSTALL_PREFIX", dest.as_ref().to_str().unwrap()) + .define("CMAKE_C_COMPILER", "clang") + .define("CMAKE_CXX_COMPILER", "clang++") + .define("CMAKE_LINKER", "ld.ldd"); + + if options.deterministic { + boring_conf + .define("FUZZ", "1") + .define("NO_FUZZER_MODE", "1"); + } + + if options.sancov { + boring_conf + .cflag("-fsanitize-coverage=trace-pc-guard") + .cxxflag("-fsanitize-coverage=trace-pc-guard"); + } + + if options.gcov_analysis { + boring_conf + .cflag("-ftest-coverage") + .cflag("-fprofile-arcs") + .cxxflag("-ftest-coverage") + .cxxflag("-fprofile-arcs"); + } + + if options.llvm_cov_analysis { + boring_conf + .cflag("-fprofile-instr-generate") + .cflag("-fcoverage-mapping") + .cxxflag("-fprofile-instr-generate") + .cxxflag("-fcoverage-mapping"); + } + + if options.asan { + let output = Command::new("clang") + .args(["--print-resource-dir"]) + .output() + .expect("failed to clang to get resource dir"); + let clang: &str = std::str::from_utf8(&output.stdout).unwrap().trim(); + + // Important: Make sure to pass these flags to the linker invoked by rustc! + boring_conf + .cflag("-fsanitize=address") + .cflag("-shared-libsan") + .cflag("-Wno-unused-command-line-argument") + .cflag(format!("-Wl,-rpath={}/lib/linux/", clang)) + .cxxflag("-fsanitize=address") + .cxxflag("-shared-libsan") + .cxxflag("-Wno-unused-command-line-argument") + .cxxflag(format!("-Wl,-rpath={}/lib/linux/", clang)); + } + + boring_conf.build(); + dest.as_ref().to_owned() +} + +pub fn build(options: &BoringSSLOptions) -> std::io::Result<()> { + clone_boringssl(&options.source_dir, options)?; + + let _ = build_boringssl(&options.out_dir, options); + + // let bindings = bindgen::Builder::default() + // .size_t_is_usize(false) + // .header(format!("{}/wrapper.h", options.source_dir.display())) + // .header(format!("{}/wolfssl/internal.h", out_dir.display())) + // .clang_arg(format!("-I{}/include/", out_dir.display())) + // .parse_callbacks(Box::new(ignored_macros)) + // .rustfmt_bindings(true) + // .generate() + // .expect("Unable to generate bindings"); + + // bindings + // .write_to_file(dst.join("bindings.rs")) + // .expect("Couldn't write bindings!"); + + Ok(()) +} diff --git a/crates/boringssl-src/src/main.rs b/crates/boringssl-src/src/main.rs new file mode 100644 index 000000000..49a23d8a5 --- /dev/null +++ b/crates/boringssl-src/src/main.rs @@ -0,0 +1,28 @@ +use std::{env, path::PathBuf}; + +use boringssl_src::{build, BoringSSLOptions}; + +fn main() { + // let out_dir = PathBuf::from("out_dir"); + let out_dir = PathBuf::from(env::var("CARGO_MANIFEST_DIR").unwrap()).join("out"); + + env::set_var("TARGET", "aarch64-apple-darwin"); + env::set_var("HOST", "aarch64-apple-darwin"); + env::set_var("OPT_LEVEL", "3"); + env::set_var( + "OUT_DIR", + &out_dir.canonicalize().unwrap().display().to_string(), + ); + + build(&BoringSSLOptions { + gcov_analysis: false, + llvm_cov_analysis: false, + deterministic: false, + asan: false, + sancov: true, + git_ref: "master".to_string(), + out_dir, + source_dir: PathBuf::from(env::var("CARGO_MANIFEST_DIR").unwrap()).join("boringssl"), + }) + .unwrap(); +} diff --git a/crates/boringssl-src/wrapper.h b/crates/boringssl-src/wrapper.h new file mode 100644 index 000000000..08dcfda3b --- /dev/null +++ b/crates/boringssl-src/wrapper.h @@ -0,0 +1,3 @@ +#include +#include +#include diff --git a/crates/boringssl-sys/Cargo.toml b/crates/boringssl-sys/Cargo.toml new file mode 100644 index 000000000..d4a1da9c4 --- /dev/null +++ b/crates/boringssl-sys/Cargo.toml @@ -0,0 +1,23 @@ +[package] +name = "boringssl-sys" +version = "0.1.0" +edition = "2021" +authors = [] +links = "boring" + +[build-dependencies] +boringssl-src = { path = "../boringssl-src" } +bindgen = "0.69.1" + +[features] + + +vendored-master = [] + +sancov = [] +asan = [] +deterministic = [] + + +gcov_analysis = [] +llvm_cov_analysis = [] diff --git a/crates/boringssl-sys/build.rs b/crates/boringssl-sys/build.rs new file mode 100644 index 000000000..ff5a8fc51 --- /dev/null +++ b/crates/boringssl-sys/build.rs @@ -0,0 +1,94 @@ +use std::{env, path::PathBuf}; + +use boringssl_src::{build, BoringSSLOptions}; + +const REF: &str = if cfg!(feature = "vendored-master") { + "master" +} else { + "master" +}; + +fn main() { + let source_dir = + PathBuf::from(env::var("CARGO_MANIFEST_DIR").unwrap()).join("../boringssl-src/boringssl"); + let out_dir = PathBuf::from(env::var("OUT_DIR").unwrap()).join("boring"); + build(&BoringSSLOptions { + asan: cfg!(feature = "asan"), + sancov: cfg!(feature = "sancov"), + gcov_analysis: cfg!(feature = "gcov_analysis"), + llvm_cov_analysis: cfg!(feature = "llvm_cov_analysis"), + deterministic: cfg!(feature = "deterministic"), + git_ref: REF.to_string(), + out_dir: out_dir.clone(), + source_dir: source_dir.clone(), + }) + .unwrap(); + + println!("cargo:rustc-link-search={}/lib", out_dir.display()); + println!("cargo:rustc-link-lib=static=crypto"); + println!("cargo:rustc-link-lib=static=ssl"); + + // Linking Time! + let include_path = out_dir.join("include"); + + let bindings_out_dir = PathBuf::from(env::var("OUT_DIR").unwrap()); + + let mut builder = bindgen::Builder::default() + .derive_copy(true) + .derive_debug(true) + .derive_default(true) + .derive_eq(true) + .default_enum_style(bindgen::EnumVariation::NewType { + is_bitfield: false, + is_global: false, + }) + .default_macro_constant_type(bindgen::MacroTypeVariation::Signed) + .generate_comments(true) + .fit_macro_constants(false) + .size_t_is_usize(true) + .layout_tests(true) + .prepend_enum_name(true) + .clang_arg("-I") + .clang_arg(include_path.display().to_string()); + + let headers = [ + "aes.h", + "asn1_mac.h", + "asn1t.h", + "blake2.h", + "blowfish.h", + "cast.h", + "chacha.h", + "cmac.h", + "cpu.h", + "curve25519.h", + "des.h", + "dtls1.h", + "hkdf.h", + "hmac.h", + "hrss.h", + "md4.h", + "md5.h", + "obj_mac.h", + "objects.h", + "opensslv.h", + "ossl_typ.h", + "pkcs12.h", + "poly1305.h", + "rand.h", + "rc4.h", + "ripemd.h", + "siphash.h", + "srtp.h", + "trust_token.h", + "x509v3.h", + ]; + for header in &headers { + builder = builder.header(include_path.join("openssl").join(header).to_str().unwrap()); + } + + let bindings = builder.generate().expect("Unable to generate bindings"); + bindings + .write_to_file(bindings_out_dir.join("bindings.rs")) + .expect("Couldn't write bindings!"); +} diff --git a/crates/boringssl-sys/src/lib.rs b/crates/boringssl-sys/src/lib.rs new file mode 100644 index 000000000..52ffd7c01 --- /dev/null +++ b/crates/boringssl-sys/src/lib.rs @@ -0,0 +1,89 @@ +#![allow( + clippy::missing_safety_doc, + clippy::redundant_static_lifetimes, + clippy::too_many_arguments, + clippy::unreadable_literal, + clippy::upper_case_acronyms, + improper_ctypes, + non_camel_case_types, + non_snake_case, + non_upper_case_globals, + unused_imports +)] +#![cfg_attr(docsrs, feature(doc_auto_cfg))] + +/* +This file is derived from https://github.com/cloudflare/boring/blob/cdb76dcba2cd0f36bc9ba94c1a3b23137a8bbfe5/boring-sys/src/lib.rs +And is dual licensed under the terms of both the Apache License, Version 2.0 and +the MIT license without any additional terms or conditions. +*/ + +use std::convert::TryInto; +use std::ffi::c_void; +use std::os::raw::{c_char, c_int, c_uint, c_ulong}; + +#[allow(clippy::useless_transmute, clippy::derive_partial_eq_without_eq)] +mod generated { + include!(concat!(env!("OUT_DIR"), "/bindings.rs")); +} +pub use generated::*; + +#[cfg(target_pointer_width = "64")] +pub type BN_ULONG = u64; +#[cfg(target_pointer_width = "32")] +pub type BN_ULONG = u32; + +#[cfg(const_fn)] +macro_rules! const_fn { + ($(pub const fn $name:ident($($arg:ident: $t:ty),*) -> $ret:ty $b:block)*) => { + $( + pub const fn $name($($arg: $t),*) -> $ret $b + )* + } +} + +#[cfg(not(const_fn))] +macro_rules! const_fn { + ($(pub const fn $name:ident($($arg:ident: $t:ty),*) -> $ret:ty $b:block)*) => { + $( + pub fn $name($($arg: $t),*) -> $ret $b + )* + } +} + +const_fn! { + pub const fn ERR_PACK(l: c_int, f: c_int, r: c_int) -> c_ulong { + ((l as c_ulong & 0x0FF) << 24) | + ((f as c_ulong & 0xFFF) << 12) | + (r as c_ulong & 0xFFF) + } + + pub const fn ERR_GET_LIB(l: c_uint) -> c_int { + ((l >> 24) & 0x0FF) as c_int + } + + pub const fn ERR_GET_FUNC(l: c_uint) -> c_int { + ((l >> 12) & 0xFFF) as c_int + } + + pub const fn ERR_GET_REASON(l: c_uint) -> c_int { + (l & 0xFFF) as c_int + } +} + +pub fn init() { + use std::ptr; + use std::sync::Once; + + // explicitly initialize to work around https://github.com/openssl/openssl/issues/3505 + static INIT: Once = Once::new(); + + let init_options = OPENSSL_INIT_LOAD_SSL_STRINGS; + + INIT.call_once(|| { + assert_eq!( + unsafe { OPENSSL_init_ssl(init_options.try_into().unwrap(), ptr::null_mut()) }, + 1 + ) + }); +} From babc29df15cece28bc07d61bea9bbeacddb64ee6 Mon Sep 17 00:00:00 2001 From: Tom Gouville Date: Sun, 12 Nov 2023 18:53:28 +0100 Subject: [PATCH 03/63] cleaning + doc --- crates/boringssl-src/Cargo.toml | 9 --------- crates/boringssl-src/src/lib.rs | 17 ++--------------- crates/boringssl-sys/Cargo.toml | 4 ---- crates/boringssl-sys/build.rs | 4 +++- crates/boringssl-sys/src/lib.rs | 9 +++------ 5 files changed, 8 insertions(+), 35 deletions(-) diff --git a/crates/boringssl-src/Cargo.toml b/crates/boringssl-src/Cargo.toml index 1f000f300..b01df7117 100644 --- a/crates/boringssl-src/Cargo.toml +++ b/crates/boringssl-src/Cargo.toml @@ -4,14 +4,5 @@ version = "0.1.0" edition = "2021" [dependencies] -bindgen = "0.69.1" -# autotools = "0.2.6" -# cfg-if = "1.0.0" - -# [[bin]] -# name = "build_static" -# path = "src/main.rs" - cmake = "0.1" -[build-dependencies] diff --git a/crates/boringssl-src/src/lib.rs b/crates/boringssl-src/src/lib.rs index 19ed0faef..73562e50f 100644 --- a/crates/boringssl-src/src/lib.rs +++ b/crates/boringssl-src/src/lib.rs @@ -53,7 +53,6 @@ fn _patch_boringssl>( } fn clone_boringssl>(dest: &P, options: &BoringSSLOptions) -> std::io::Result<()> { - //return Ok(()); std::fs::remove_dir_all(dest)?; let status = Command::new("git") .arg("clone") @@ -73,6 +72,8 @@ fn clone_boringssl>(dest: &P, options: &BoringSSLOptions) -> std: } fn build_boringssl>(dest: &P, options: &BoringSSLOptions) -> PathBuf { + // BoringSSL is written in C and C++, so all flags have to be given with + // cflags and cxxflags let mut boring_conf = Config::new(&options.source_dir); boring_conf .define("BUILD_SHARED_LIBS", "OFF") @@ -137,19 +138,5 @@ pub fn build(options: &BoringSSLOptions) -> std::io::Result<()> { let _ = build_boringssl(&options.out_dir, options); - // let bindings = bindgen::Builder::default() - // .size_t_is_usize(false) - // .header(format!("{}/wrapper.h", options.source_dir.display())) - // .header(format!("{}/wolfssl/internal.h", out_dir.display())) - // .clang_arg(format!("-I{}/include/", out_dir.display())) - // .parse_callbacks(Box::new(ignored_macros)) - // .rustfmt_bindings(true) - // .generate() - // .expect("Unable to generate bindings"); - - // bindings - // .write_to_file(dst.join("bindings.rs")) - // .expect("Couldn't write bindings!"); - Ok(()) } diff --git a/crates/boringssl-sys/Cargo.toml b/crates/boringssl-sys/Cargo.toml index d4a1da9c4..f172b9112 100644 --- a/crates/boringssl-sys/Cargo.toml +++ b/crates/boringssl-sys/Cargo.toml @@ -2,7 +2,6 @@ name = "boringssl-sys" version = "0.1.0" edition = "2021" -authors = [] links = "boring" [build-dependencies] @@ -10,14 +9,11 @@ boringssl-src = { path = "../boringssl-src" } bindgen = "0.69.1" [features] - - vendored-master = [] sancov = [] asan = [] deterministic = [] - gcov_analysis = [] llvm_cov_analysis = [] diff --git a/crates/boringssl-sys/build.rs b/crates/boringssl-sys/build.rs index ff5a8fc51..28fc286df 100644 --- a/crates/boringssl-sys/build.rs +++ b/crates/boringssl-sys/build.rs @@ -24,11 +24,13 @@ fn main() { }) .unwrap(); + // Linking Time! + // The parameters to link BoringSSL are inspired from + // https://github.com/cloudflare/boring/blob/master/boring-sys/build/main.rs println!("cargo:rustc-link-search={}/lib", out_dir.display()); println!("cargo:rustc-link-lib=static=crypto"); println!("cargo:rustc-link-lib=static=ssl"); - // Linking Time! let include_path = out_dir.join("include"); let bindings_out_dir = PathBuf::from(env::var("OUT_DIR").unwrap()); diff --git a/crates/boringssl-sys/src/lib.rs b/crates/boringssl-sys/src/lib.rs index 52ffd7c01..5fc4e3c10 100644 --- a/crates/boringssl-sys/src/lib.rs +++ b/crates/boringssl-sys/src/lib.rs @@ -12,12 +12,9 @@ )] #![cfg_attr(docsrs, feature(doc_auto_cfg))] -/* -This file is derived from https://github.com/cloudflare/boring/blob/cdb76dcba2cd0f36bc9ba94c1a3b23137a8bbfe5/boring-sys/src/lib.rs -And is dual licensed under the terms of both the Apache License, Version 2.0 and -the MIT license without any additional terms or conditions. -*/ - +/// This file is derived from https://github.com/cloudflare/boring/blob/cdb76dcba2cd0f36bc9ba94c1a3b23137a8bbfe5/boring-sys/src/lib.rs +/// And is dual licensed under the terms of both the Apache License, Version 2.0 and +/// the MIT license without any additional terms or conditions. use std::convert::TryInto; use std::ffi::c_void; use std::os::raw::{c_char, c_int, c_uint, c_ulong}; From 0008af89cdc35b74ce61e2ea9d089c6afd99aaf7 Mon Sep 17 00:00:00 2001 From: Tom Gouville Date: Mon, 13 Nov 2023 16:50:13 +0100 Subject: [PATCH 04/63] add missing dependency --- crates/boringssl-src/Cargo.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/crates/boringssl-src/Cargo.toml b/crates/boringssl-src/Cargo.toml index b01df7117..fbdaf9b76 100644 --- a/crates/boringssl-src/Cargo.toml +++ b/crates/boringssl-src/Cargo.toml @@ -5,4 +5,5 @@ edition = "2021" [dependencies] cmake = "0.1" +bindgen = "0.69.1" From 0336b2b9211c45b2ca42a43b22b9a9ba8f82302d Mon Sep 17 00:00:00 2001 From: Tom Gouville Date: Wed, 22 Nov 2023 10:35:04 +0100 Subject: [PATCH 05/63] WIP boringssl integration --- Cargo.lock | 2 + crates/boring/README.md | 1 - crates/boring/src/ssl/mod.rs | 14 + crates/boringssl-src/src/lib.rs | 10 +- tlspuffin/Cargo.toml | 25 +- tlspuffin/src/boringssl/bindings.rs | 94 +++++ tlspuffin/src/boringssl/deterministic.rs | 35 ++ tlspuffin/src/boringssl/mod.rs | 487 +++++++++++++++++++++++ tlspuffin/src/boringssl/util.rs | 87 ++++ tlspuffin/src/lib.rs | 2 + tlspuffin/src/put_registry.rs | 5 + 11 files changed, 752 insertions(+), 10 deletions(-) delete mode 120000 crates/boring/README.md create mode 100644 tlspuffin/src/boringssl/bindings.rs create mode 100644 tlspuffin/src/boringssl/deterministic.rs create mode 100644 tlspuffin/src/boringssl/mod.rs create mode 100644 tlspuffin/src/boringssl/util.rs diff --git a/Cargo.lock b/Cargo.lock index 05db10867..6a4c96c67 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2387,6 +2387,8 @@ dependencies = [ name = "tlspuffin" version = "0.1.0" dependencies = [ + "boring", + "boringssl-sys", "cfg-if", "criterion", "env_logger 0.9.3", diff --git a/crates/boring/README.md b/crates/boring/README.md deleted file mode 120000 index 32d46ee88..000000000 --- a/crates/boring/README.md +++ /dev/null @@ -1 +0,0 @@ -../README.md \ No newline at end of file diff --git a/crates/boring/src/ssl/mod.rs b/crates/boring/src/ssl/mod.rs index 1420ee327..84d0adb08 100644 --- a/crates/boring/src/ssl/mod.rs +++ b/crates/boring/src/ssl/mod.rs @@ -3286,6 +3286,20 @@ impl SslRef { Ok(()) } + + /// Configure as an outgoing stream from a client. + pub fn set_connect_state(&mut self) { + unsafe { ffi::SSL_set_connect_state(self.as_ptr()) } + } + + /// Configure as an incoming stream to a server. + pub fn set_accept_state(&mut self) { + unsafe { ffi::SSL_set_accept_state(self.as_ptr()) } + } + + pub fn clear(&mut self) -> i32 { + unsafe { ffi::SSL_clear(self.as_ptr()) } + } } /// An SSL stream midway through the handshake process. diff --git a/crates/boringssl-src/src/lib.rs b/crates/boringssl-src/src/lib.rs index 73562e50f..8dc7f2ca6 100644 --- a/crates/boringssl-src/src/lib.rs +++ b/crates/boringssl-src/src/lib.rs @@ -80,7 +80,9 @@ fn build_boringssl>(dest: &P, options: &BoringSSLOptions) -> Path .define("CMAKE_INSTALL_PREFIX", dest.as_ref().to_str().unwrap()) .define("CMAKE_C_COMPILER", "clang") .define("CMAKE_CXX_COMPILER", "clang++") - .define("CMAKE_LINKER", "ld.ldd"); + .pic(true) + .cflag("-g") + .cxxflag("-g"); if options.deterministic { boring_conf @@ -121,12 +123,10 @@ fn build_boringssl>(dest: &P, options: &BoringSSLOptions) -> Path boring_conf .cflag("-fsanitize=address") .cflag("-shared-libsan") - .cflag("-Wno-unused-command-line-argument") - .cflag(format!("-Wl,-rpath={}/lib/linux/", clang)) .cxxflag("-fsanitize=address") .cxxflag("-shared-libsan") - .cxxflag("-Wno-unused-command-line-argument") - .cxxflag(format!("-Wl,-rpath={}/lib/linux/", clang)); + .define("OPENSSL_NO_BUF_FREELISTS", "1") + .define("OPENSSL_NO_ASM", "1"); } boring_conf.build(); diff --git a/tlspuffin/Cargo.toml b/tlspuffin/Cargo.toml index a2f033717..4169642f4 100644 --- a/tlspuffin/Cargo.toml +++ b/tlspuffin/Cargo.toml @@ -166,16 +166,28 @@ wolfsslmaster = [ ] +boringsslmaster = [ + "boringssl-sys/vendored-master", + "tls12", + "tls13", + "boringssl-binding", + "tls12-session-resumption", + "tls13-session-resumption", + "transcript-extraction", + "client-authentication-transcript-extraction", + "deterministic", +] + # Logs each execution of __sanitizer_cov_trace_pc_guard* sancov_pcguard_log = ["puffin/sancov_pcguard_log"] # Uses libafl for the instrumentation. sancov_pcguard_log and sancov_libafl are mutally exclusive -sancov_libafl = ["puffin/sancov_libafl", "openssl-src?/sancov", "wolfssl-sys?/sancov"] +sancov_libafl = ["puffin/sancov_libafl", "openssl-src?/sancov", "wolfssl-sys?/sancov", "boringssl-sys?/sancov"] # Enables ASAN -asan = ["openssl-src?/asan", "wolfssl-sys?/asan"] +asan = ["openssl-src?/asan", "wolfssl-sys?/asan", "boringssl-sys?/asan"] -gcov_analysis = ["openssl-src?/gcov_analysis", "wolfssl-sys?/gcov_analysis"] -llvm_cov_analysis = ["openssl-src?/llvm_cov_analysis", "wolfssl-sys?/llvm_cov_analysis"] +gcov_analysis = ["openssl-src?/gcov_analysis", "wolfssl-sys?/gcov_analysis", "wolfssl-sys?/gcov_analysis"] +llvm_cov_analysis = ["openssl-src?/llvm_cov_analysis", "wolfssl-sys?/llvm_cov_analysis", "boringssl-sys?/llvm_cov_analysis"] # WolfSSL bindings are used wolfssl-binding = ["wolfssl", "wolfssl-sys", "foreign-types"] @@ -188,6 +200,8 @@ openssl102-binding = ["openssl-binding"] # Openssl 1.1.1 bindings openssl111-binding = ["openssl-binding"] +boringssl-binding = ["boring", "boringssl-sys", "foreign-types"] + # PUT supports TLS 1.3 tls13 = [] # PUT supports TLS 1.2 @@ -243,6 +257,9 @@ wolfssl = { path = "../crates/wolfssl", optional = true } wolfssl-sys = { path = "../crates/wolfssl-sys", features = [], optional = true } foreign-types = { version = "0.5.0", optional = true } +boring = { path = "../crates/boring", optional = true } +boringssl-sys = { path = "../crates/boringssl-sys", features = [], optional = true } + [build-dependencies] # It is essential that this dependency is listed as build-dependencies! Because it is one. Else features get resolved wrong. openssl-src = { version = "*", features = [], optional = true } diff --git a/tlspuffin/src/boringssl/bindings.rs b/tlspuffin/src/boringssl/bindings.rs new file mode 100644 index 000000000..4f6dd08c7 --- /dev/null +++ b/tlspuffin/src/boringssl/bindings.rs @@ -0,0 +1,94 @@ +use std::ffi::c_void; + +use boring::{ + error::ErrorStack, + pkey::Private, + rsa::Rsa, + ssl::{SslContext, SslContextBuilder, SslContextRef, SslRef}, +}; +use boringssl_sys::{SSL_CTX_set_options, SSL_clear, RSA, SSL, SSL_CTX}; +// use foreign_types_openssl::ForeignTypeRef; +use libc::{c_int, c_long, c_ulong}; + +const SSL_OP_ALLOW_NO_DHE_KEX: c_ulong = 0x00000400; +const SSL_CTRL_SET_TMP_RSA: c_int = 2; + +// unsafe fn SSL_CTX_set_tmp_rsa(ctx: *mut SSL_CTX, key: *mut RSA) -> c_long { +// SSL_CTX_ctrl(ctx, SSL_CTRL_SET_TMP_RSA, 0, key as *mut c_void) +// } + +// extern "C" { +// fn SSL_clear(ssl: *mut SSL) -> c_int; + +// #[cfg(not(feature = "openssl111-binding"))] +// fn SSL_CTX_set_tmp_rsa_callback( +// ctx: *mut SSL_CTX, +// ecdh: unsafe extern "C" fn(ssl: *mut SSL, is_export: c_int, keylength: c_int) -> *mut RSA, +// ); +// } + +// #[cfg(all( +// any(feature = "openssl101-binding", feature = "openssl102-binding"), +// not(feature = "openssl111-binding") +// ))] +// unsafe extern "C" fn raw_tmp_rsa(ssl: *mut SSL, is_export: c_int, keylength: c_int) -> *mut RSA +// where +// F: Fn(&mut SslRef, bool, u32) -> Result, ErrorStack> + 'static + Sync + Send, +// { +// let ssl = SslRef::from_ptr_mut(ssl); +// let callback = ssl +// .ssl_context() +// .ex_data(SslContext::new_ex_index::().unwrap()) +// .expect("BUG: tmp rsa callback missing") as *const F; + +// match (*callback)(ssl, is_export != 0, keylength as u32) { +// Ok(rsa_key) => { +// let ptr = rsa_key.as_ptr(); +// std::mem::forget(rsa_key); +// ptr +// } +// Err(e) => { +// e.put(); +// std::ptr::null_mut() +// } +// } +// } + +// #[cfg(all( +// any(feature = "openssl101-binding", feature = "openssl102-binding"), +// not(feature = "openssl111-binding") +// ))] +// pub fn set_tmp_rsa_callback(ctx: &mut SslContextBuilder, callback: F) +// where +// F: Fn(&mut SslRef, bool, u32) -> Result, ErrorStack> + 'static + Sync + Send, +// { +// unsafe { +// ctx.set_ex_data(SslContext::new_ex_index::().unwrap(), callback); +// SSL_CTX_set_tmp_rsa_callback(ctx.as_ptr(), raw_tmp_rsa::); +// } +// } + +// /// Sets the parameters to be used during ephemeral RSA key exchange. +// /// +// /// This corresponds to `SSL_CTX_set_tmp_rsa`. +// pub fn set_tmp_rsa(ctx: &SslContextBuilder, key: &Rsa) -> Result<(), ErrorStack> { +// unsafe { cvt(SSL_CTX_set_tmp_rsa(ctx.as_ptr(), key.as_ptr()) as c_int).map(|_| ()) } +// } + +// fn cvt(r: c_int) -> Result { +// if r <= 0 { +// Err(ErrorStack::get()) +// } else { +// Ok(r) +// } +// } + +// /// In TLSv1.3 allow a non-(ec)dhe based key exchange mode on resumption. +// /// This means that there will be no forward secrecy for the resumed session. +// pub fn set_allow_no_dhe_kex(ctx: &mut SslContextBuilder) { +// unsafe { SSL_CTX_set_options(ctx.as_ptr(), SSL_OP_ALLOW_NO_DHE_KEX) }; +// } + +pub fn clear(ssl: &mut SslRef) -> u32 { + unsafe { SSL_clear(&mut ssl) as u32 } +} diff --git a/tlspuffin/src/boringssl/deterministic.rs b/tlspuffin/src/boringssl/deterministic.rs new file mode 100644 index 000000000..9395f4a78 --- /dev/null +++ b/tlspuffin/src/boringssl/deterministic.rs @@ -0,0 +1,35 @@ +use std::os::raw::c_int; + +use log::warn; + +#[cfg(feature = "deterministic")] +extern "C" { + fn make_openssl_deterministic(); + fn RAND_seed(buf: *mut u8, num: c_int); +} + +#[cfg(feature = "deterministic")] +pub fn set_openssl_deterministic() { + warn!("OpenSSL is no longer random!"); + unsafe { + make_openssl_deterministic(); + let mut seed: [u8; 4] = 42u32.to_le().to_ne_bytes(); + let buf = seed.as_mut_ptr(); + RAND_seed(buf, 4); + } +} + +#[cfg(test)] +mod tests { + use openssl::rand::rand_bytes; + + #[test] + #[cfg(feature = "openssl111-binding")] + fn test_openssl_no_randomness() { + use crate::openssl::deterministic::set_openssl_deterministic; + set_openssl_deterministic(); + let mut buf1 = [0; 2]; + rand_bytes(&mut buf1).unwrap(); + assert_eq!(buf1, [70, 100]); + } +} diff --git a/tlspuffin/src/boringssl/mod.rs b/tlspuffin/src/boringssl/mod.rs new file mode 100644 index 000000000..d28896284 --- /dev/null +++ b/tlspuffin/src/boringssl/mod.rs @@ -0,0 +1,487 @@ +use std::{ + any::Any, + cell::RefCell, + fmt::{Debug, Formatter}, + io, + io::ErrorKind, + rc::Rc, +}; + +use boring::{ + error::ErrorStack, + pkey::{PKeyRef, Private}, + ssl::{Ssl, SslContext, SslMethod, SslOptions, SslStream, SslVerifyMode}, + stack::Stack, + x509::{ + store::{X509Store, X509StoreBuilder}, + X509Ref, X509StoreContext, X509, + }, +}; +use log::{info, warn}; +use puffin::{ + agent::{AgentDescriptor, AgentName, AgentType, TLSVersion}, + error::Error, + protocol::MessageResult, + put::{Put, PutDescriptor, PutName}, + put_registry::Factory, + stream::{MemoryStream, Stream}, + trace::TraceContext, +}; +use smallvec::SmallVec; + +use crate::{ + boringssl::util::{set_max_protocol_version, static_rsa_cert}, + claims::{ + ClaimData, ClaimDataMessage, ClaimDataTranscript, ClientHello, Finished, TlsClaim, + TlsTranscript, TranscriptCertificate, TranscriptClientFinished, TranscriptClientHello, + TranscriptPartialClientHello, TranscriptServerFinished, TranscriptServerHello, + }, + protocol::TLSProtocolBehavior, + put::TlsPutConfig, + put_registry::BORINGSSL_PUT, + static_certs::{ALICE_CERT, ALICE_PRIVATE_KEY, BOB_CERT, BOB_PRIVATE_KEY, EVE_CERT}, + tls::rustls::msgs::{ + deframer::MessageDeframer, + message::{Message, OpaqueMessage}, + }, +}; + +// mod bindings; +// #[cfg(feature = "deterministic")] +// mod deterministic; +mod util; + +/* + Change openssl version: + cargo clean -p openssl-src + cd openssl-src/openssl + git checkout OpenSSL_1_1_1j +*/ + +pub fn new_boringssl_factory() -> Box> { + struct BoringSSLFactory; + impl Factory for BoringSSLFactory { + fn create( + &self, + context: &TraceContext, + agent_descriptor: &AgentDescriptor, + ) -> Result>, Error> { + let put_descriptor = context.put_descriptor(agent_descriptor); + + let options = &put_descriptor.options; + + let use_clear = options + .get_option("use_clear") + .map(|value| value.parse().unwrap_or(false)) + .unwrap_or(false); + + // FIXME: Add non-clear method like in wolfssl + if !use_clear { + info!("OpenSSL put does not support clearing mode") + } + + let config = TlsPutConfig { + descriptor: agent_descriptor.clone(), + claims: context.claims().clone(), + authenticate_peer: agent_descriptor.typ == AgentType::Client + && agent_descriptor.server_authentication + || agent_descriptor.typ == AgentType::Server + && agent_descriptor.client_authentication, + extract_deferred: Rc::new(RefCell::new(None)), + use_clear, + }; + Ok(Box::new(BoringSSL::new(config).map_err(|err| { + Error::Put(format!("Failed to create client/server: {}", err)) + })?)) + } + + fn name(&self) -> PutName { + BORINGSSL_PUT + } + + fn version(&self) -> String { + BoringSSL::version() + } + } + + Box::new(BoringSSLFactory) +} + +pub struct BoringSSL { + stream: SslStream>, + config: TlsPutConfig, +} + +impl Drop for BoringSSL { + fn drop(&mut self) { + #[cfg(feature = "claims")] + self.deregister_claimer(); + } +} + +impl Stream for BoringSSL { + fn add_to_inbound(&mut self, result: &OpaqueMessage) { + as Stream>::add_to_inbound( + self.stream.get_mut(), + result, + ) + } + + fn take_message_from_outbound( + &mut self, + ) -> Result>, Error> { + let memory_stream = self.stream.get_mut(); + //memory_stream.take_message_from_outbound() + + MemoryStream::take_message_from_outbound(memory_stream) + } +} + +fn to_claim_data(protocol_version: TLSVersion, claim: security_claims::Claim) -> Option { + match claim.typ { + // Transcripts + security_claims::ClaimType::CLAIM_TRANSCRIPT_CH => Some(ClaimData::Transcript( + ClaimDataTranscript::ClientHello(TranscriptClientHello(TlsTranscript( + claim.transcript.data, + claim.transcript.length, + ))), + )), + security_claims::ClaimType::CLAIM_TRANSCRIPT_PARTIAL_CH => Some(ClaimData::Transcript( + ClaimDataTranscript::PartialClientHello(TranscriptPartialClientHello(TlsTranscript( + claim.transcript.data, + claim.transcript.length, + ))), + )), + security_claims::ClaimType::CLAIM_TRANSCRIPT_CH_SH => Some(ClaimData::Transcript( + ClaimDataTranscript::ServerHello(TranscriptServerHello(TlsTranscript( + claim.transcript.data, + claim.transcript.length, + ))), + )), + security_claims::ClaimType::CLAIM_TRANSCRIPT_CH_SERVER_FIN => Some(ClaimData::Transcript( + ClaimDataTranscript::ServerFinished(TranscriptServerFinished(TlsTranscript( + claim.transcript.data, + claim.transcript.length, + ))), + )), + security_claims::ClaimType::CLAIM_TRANSCRIPT_CH_CLIENT_FIN => Some(ClaimData::Transcript( + ClaimDataTranscript::ClientFinished(TranscriptClientFinished(TlsTranscript( + claim.transcript.data, + claim.transcript.length, + ))), + )), + security_claims::ClaimType::CLAIM_TRANSCRIPT_CH_CERT => Some(ClaimData::Transcript( + ClaimDataTranscript::Certificate(TranscriptCertificate(TlsTranscript( + claim.transcript.data, + claim.transcript.length, + ))), + )), + // Messages + // Transcripts in these messages are not up-to-date. They get updated after the Message has + // been processed + security_claims::ClaimType::CLAIM_FINISHED => { + Some(ClaimData::Message(ClaimDataMessage::Finished(Finished { + outbound: claim.write > 0, + client_random: SmallVec::from(claim.client_random.data), + server_random: SmallVec::from(claim.server_random.data), + session_id: SmallVec::from_slice( + &claim.session_id.data[..claim.session_id.length as usize], + ), + authenticate_peer: false, // FIXME + peer_certificate: Default::default(), // FIXME + master_secret: match protocol_version { + TLSVersion::V1_3 => SmallVec::from_slice(&claim.master_secret.secret), + TLSVersion::V1_2 => SmallVec::from_slice(&claim.master_secret_12.secret), + }, + chosen_cipher: claim.chosen_cipher.data, + available_ciphers: SmallVec::from_iter( + claim.available_ciphers.ciphers[..claim.available_ciphers.length as usize] + .iter() + .map(|cipher| cipher.data), + ), + signature_algorithm: claim.signature_algorithm, + peer_signature_algorithm: claim.peer_signature_algorithm, + }))) + } + security_claims::ClaimType::CLAIM_CLIENT_HELLO => None, + security_claims::ClaimType::CLAIM_CCS => None, + security_claims::ClaimType::CLAIM_END_OF_EARLY_DATA => None, + security_claims::ClaimType::CLAIM_CERTIFICATE => None, + security_claims::ClaimType::CLAIM_KEY_EXCHANGE => None, + // FIXME it is weird that this returns the correct transcript + security_claims::ClaimType::CLAIM_CERTIFICATE_VERIFY => { + if claim.write == 0 { + Some(ClaimData::Transcript(ClaimDataTranscript::ServerFinished( + TranscriptServerFinished(TlsTranscript( + claim.transcript.data, + claim.transcript.length, + )), + ))) + } else { + None + } + } + security_claims::ClaimType::CLAIM_KEY_UPDATE => None, + security_claims::ClaimType::CLAIM_HELLO_REQUEST => None, + security_claims::ClaimType::CLAIM_SERVER_HELLO => None, + security_claims::ClaimType::CLAIM_CERTIFICATE_REQUEST => None, + security_claims::ClaimType::CLAIM_SERVER_DONE => None, + security_claims::ClaimType::CLAIM_SESSION_TICKET => None, + security_claims::ClaimType::CLAIM_CERTIFICATE_STATUS => None, + security_claims::ClaimType::CLAIM_EARLY_DATA => None, + security_claims::ClaimType::CLAIM_ENCRYPTED_EXTENSIONS => None, + _ => None, + } +} + +impl Put for BoringSSL { + fn progress(&mut self, _agent_name: &AgentName) -> Result<(), Error> { + let result = if self.is_state_successful() { + // Trigger another read + let mut vec: Vec = Vec::from([1; 128]); + let maybe_error: MaybeError = self.stream.ssl_read(&mut vec).into(); + maybe_error.into() + } else { + let maybe_error: MaybeError = self.stream.do_handshake().into(); + maybe_error.into() + }; + + result + } + + fn reset(&mut self, agent_name: AgentName) -> Result<(), Error> { + // bindings::clear(&mut self.stream.ssl()); // FIXME: Add non-clear method like in wolfssl + self.stream.ssl_mut().clear(); + Ok(()) + } + + fn descriptor(&self) -> &AgentDescriptor { + &self.config.descriptor + } + + #[cfg(feature = "claims")] + fn register_claimer(&mut self, agent_name: AgentName) { + unsafe { + use foreign_types_openssl::ForeignTypeRef; + + let claims = self.config.claims.clone(); + let protocol_version = self.config.descriptor.tls_version; + let origin = self.config.descriptor.typ; + + security_claims::register_claimer( + self.stream.ssl().as_ptr().cast(), + move |claim: security_claims::Claim| { + if let Some(data) = to_claim_data(protocol_version, claim) { + claims.deref_borrow_mut().claim_sized(TlsClaim { + agent_name, + origin, + protocol_version, + data, + }) + } + }, + ); + } + } + + #[cfg(feature = "claims")] + fn deregister_claimer(&mut self) { + unsafe { + use foreign_types_openssl::ForeignTypeRef; + security_claims::deregister_claimer(self.stream.ssl().as_ptr().cast()); + } + } + + #[allow(unused_variables)] + fn rename_agent(&mut self, agent_name: AgentName) -> Result<(), Error> { + #[cfg(feature = "claims")] + { + self.deregister_claimer(); + self.register_claimer(agent_name); + } + Ok(()) + } + + fn describe_state(&self) -> &str { + // Very useful for nonblocking according to docs: + // https://www.openssl.org/docs/manmaster/man3/SSL_state_string.html + // When using nonblocking sockets, the function call performing the handshake may return + // with SSL_ERROR_WANT_READ or SSL_ERROR_WANT_WRITE condition, + // so that SSL_state_string[_long]() may be called. + self.stream.ssl().state_string_long() + } + + fn is_state_successful(&self) -> bool { + self.describe_state() + .contains("SSL negotiation finished successfully") + } + + fn set_deterministic(&mut self) -> Result<(), Error> { + #[cfg(feature = "deterministic")] + { + // deterministic::set_openssl_deterministic(); + Ok(()) + } + #[cfg(not(feature = "deterministic"))] + { + Err(Error::Agent( + "Unable to make OpenSSL deterministic!".to_string(), + )) + } + } + + fn shutdown(&mut self) -> String { + panic!("Unsupported with OpenSSL PUT") + } + + fn version() -> String { + boring::version::version().to_string() + } +} + +impl BoringSSL { + fn new(config: TlsPutConfig) -> Result { + let agent_descriptor = &config.descriptor; + let mut ssl = match agent_descriptor.typ { + AgentType::Server => Self::create_server(agent_descriptor)?, + AgentType::Client => Self::create_client(agent_descriptor)?, + }; + + let stream = SslStream::new(ssl, MemoryStream::new(MessageDeframer::new()))?; + + #[cfg(feature = "claims")] + let agent_name = agent_descriptor.name; + + let mut boringssl = BoringSSL { config, stream }; + + #[cfg(feature = "claims")] + boringssl.register_claimer(agent_name); + + Ok(boringssl) + } + + fn create_server(descriptor: &AgentDescriptor) -> Result { + let mut ctx_builder = SslContext::builder(SslMethod::tls())?; + + let (cert, key) = static_rsa_cert(ALICE_PRIVATE_KEY.0.as_bytes(), ALICE_CERT.0.as_bytes())?; + ctx_builder.set_certificate(&cert)?; + ctx_builder.set_private_key(&key)?; + + if descriptor.client_authentication { + let mut store = X509StoreBuilder::new()?; + store.add_cert(X509::from_pem(BOB_CERT.0.as_bytes())?)?; + store.add_cert(X509::from_pem(EVE_CERT.0.as_bytes())?)?; + let store = store.build(); + + ctx_builder.set_verify(SslVerifyMode::PEER | SslVerifyMode::FAIL_IF_NO_PEER_CERT); + ctx_builder.set_cert_store(store); + } else { + ctx_builder.set_verify(SslVerifyMode::NONE); + } + + // #[cfg(feature = "openssl111-binding")] + // ctx_builder.clear_options(boring::ssl::SslOptions::ENABLE_MIDDLEBOX_COMPAT); + + // #[cfg(feature = "openssl111-binding")] + // bindings::set_allow_no_dhe_kex(&mut ctx_builder); + + set_max_protocol_version(&mut ctx_builder, descriptor.tls_version)?; + + #[cfg(any(feature = "openssl101-binding", feature = "openssl102-binding"))] + { + ctx_builder.set_tmp_ecdh( + &openssl::ec::EcKey::from_curve_name(openssl::nid::Nid::SECP384R1)?.as_ref(), + )?; + + bindings::set_tmp_rsa(&ctx_builder, &openssl::rsa::Rsa::generate(512)?)?; + } + + // Allow EXPORT in server + ctx_builder.set_cipher_list("ALL:EXPORT:!LOW:!aNULL:!eNULL:!SSLv2")?; + + let mut ssl = Ssl::new(&ctx_builder.build())?; + ssl.set_accept_state(); + + Ok(ssl) + } + + fn create_client(descriptor: &AgentDescriptor) -> Result { + let mut ctx_builder = SslContext::builder(SslMethod::tls())?; + // Not sure whether we want this disabled or enabled: https://github.com/tlspuffin/tlspuffin/issues/67 + // The tests become simpler if disabled to maybe that's what we want. Lets leave it default + // for now. + // https://wiki.openssl.org/index.php/TLS1.3#Middlebox_Compatibility_Mode + #[cfg(feature = "openssl111-binding")] + ctx_builder.clear_options(openssl::ssl::SslOptions::ENABLE_MIDDLEBOX_COMPAT); + + set_max_protocol_version(&mut ctx_builder, descriptor.tls_version)?; + + // Disallow EXPORT in client + ctx_builder.set_cipher_list("ALL:!EXPORT:!LOW:!aNULL:!eNULL:!SSLv2")?; + + ctx_builder.set_verify(SslVerifyMode::NONE); + + if descriptor.client_authentication { + let (cert, key) = static_rsa_cert(BOB_PRIVATE_KEY.0.as_bytes(), BOB_CERT.0.as_bytes())?; + ctx_builder.set_certificate(&cert)?; + ctx_builder.set_private_key(&key)?; + } + + if descriptor.server_authentication { + ctx_builder.set_verify(SslVerifyMode::PEER | SslVerifyMode::FAIL_IF_NO_PEER_CERT); + + let mut store = X509StoreBuilder::new()?; + store.add_cert(X509::from_pem(ALICE_CERT.0.as_bytes())?)?; + store.add_cert(X509::from_pem(EVE_CERT.0.as_bytes())?)?; + let store = store.build(); + + ctx_builder.set_cert_store(store); + } else { + ctx_builder.set_verify(SslVerifyMode::NONE); + } + + let mut ssl = Ssl::new(&ctx_builder.build())?; + ssl.set_connect_state(); + + Ok(ssl) + } +} + +pub enum MaybeError { + Ok, + Err(Error), +} + +impl From> for MaybeError { + fn from(result: Result) -> Self { + if let Err(ssl_error) = result { + if let Some(io_error) = ssl_error.io_error() { + match io_error.kind() { + ErrorKind::WouldBlock => { + // Not actually an error, we just reached the end of the stream, thrown in MemoryStream + // debug!("Would have blocked but the underlying stream is non-blocking!"); + MaybeError::Ok + } + _ => MaybeError::Err(Error::IO(format!("Unexpected IO Error: {}", io_error))), + } + } else if let Some(ssl_error) = ssl_error.ssl_error() { + // OpenSSL threw an error, that means that there should be an Alert message in the + // outbound channel + MaybeError::Err(Error::Put(ssl_error.to_string())) + } else { + MaybeError::Ok + } + } else { + MaybeError::Ok + } + } +} + +impl Into> for MaybeError { + fn into(self) -> Result<(), Error> { + match self { + MaybeError::Ok => Ok(()), + MaybeError::Err(err) => Err(err), + } + } +} diff --git a/tlspuffin/src/boringssl/util.rs b/tlspuffin/src/boringssl/util.rs new file mode 100644 index 000000000..4356e7ae6 --- /dev/null +++ b/tlspuffin/src/boringssl/util.rs @@ -0,0 +1,87 @@ +use boring::{ + asn1::Asn1Time, + bn::{BigNum, MsbOption}, + error::ErrorStack, + hash::MessageDigest, + pkey::{PKey, Private}, + ssl::{SslContextBuilder, SslVersion}, + x509::{ + extension::{BasicConstraints, KeyUsage, SubjectKeyIdentifier}, + X509NameBuilder, X509, + }, +}; +use puffin::agent::TLSVersion; + +use crate::static_certs::{ALICE_CERT, ALICE_PRIVATE_KEY}; + +// FIXME: remove or use +pub fn generate_cert() -> Result<(X509, PKey), ErrorStack> { + let rsa = boring::rsa::Rsa::generate(2048)?; + let pkey = PKey::from_rsa(rsa)?; + + let mut x509_name = X509NameBuilder::new()?; + x509_name.append_entry_by_text("C", "US")?; + x509_name.append_entry_by_text("ST", "TX")?; + x509_name.append_entry_by_text("O", "Some CA organization")?; + x509_name.append_entry_by_text("CN", "ca test")?; + let x509_name = x509_name.build(); + let mut cert_builder = X509::builder()?; + cert_builder.set_version(2)?; + let serial_number = { + let mut serial = BigNum::new()?; + serial.rand(159, MsbOption::MAYBE_ZERO, false)?; + serial.to_asn1_integer() + }?; + cert_builder.set_serial_number(&serial_number)?; + cert_builder.set_subject_name(&x509_name)?; + cert_builder.set_issuer_name(&x509_name)?; + cert_builder.set_pubkey(&pkey)?; + let not_before = Asn1Time::days_from_now(0)?; + cert_builder.set_not_before(¬_before)?; + let not_after = Asn1Time::days_from_now(365)?; + cert_builder.set_not_after(¬_after)?; + + let extension = BasicConstraints::new().critical().ca().build()?; + cert_builder.append_extension(extension)?; + cert_builder.append_extension( + KeyUsage::new() + .critical() + .key_cert_sign() + .crl_sign() + .build()?, + )?; + + let subject_key_identifier = + SubjectKeyIdentifier::new().build(&cert_builder.x509v3_context(None, None))?; + cert_builder.append_extension(subject_key_identifier)?; + + cert_builder.sign(&pkey, MessageDigest::sha256())?; + let cert = cert_builder.build(); + Ok((cert, pkey)) +} + +pub fn static_rsa_cert(key: &[u8], cert: &[u8]) -> Result<(X509, PKey), ErrorStack> { + let rsa = boring::rsa::Rsa::private_key_from_pem(key)?; + let pkey = PKey::from_rsa(rsa)?; + + let cert = X509::from_pem(cert)?; + Ok((cert, pkey)) +} + +#[allow(unused_variables)] +pub fn set_max_protocol_version( + ctx_builder: &mut SslContextBuilder, + tls_version: TLSVersion, +) -> Result<(), ErrorStack> { + // Old OpenSSL versions do not have this function + match tls_version { + TLSVersion::V1_3 => { + ctx_builder.set_max_proto_version(Some(SslVersion::TLS1_3))?; + // do nothing as the maximum available TLS version is 1.3 + Ok(()) + } + TLSVersion::V1_2 => ctx_builder.set_max_proto_version(Some(SslVersion::TLS1_2)), + }?; + + Ok(()) +} diff --git a/tlspuffin/src/lib.rs b/tlspuffin/src/lib.rs index 123ff90e6..f8e0a3aa9 100644 --- a/tlspuffin/src/lib.rs +++ b/tlspuffin/src/lib.rs @@ -102,6 +102,8 @@ //! ``` //! +#[cfg(feature = "boringssl-binding")] +pub mod boringssl; pub mod claims; pub mod debug; #[cfg(feature = "openssl-binding")] diff --git a/tlspuffin/src/put_registry.rs b/tlspuffin/src/put_registry.rs index cc937c72a..4a62a53d4 100644 --- a/tlspuffin/src/put_registry.rs +++ b/tlspuffin/src/put_registry.rs @@ -7,6 +7,7 @@ use crate::protocol::TLSProtocolBehavior; pub const OPENSSL111_PUT: PutName = PutName(['O', 'P', 'E', 'N', 'S', 'S', 'L', '1', '1', '1']); pub const WOLFSSL520_PUT: PutName = PutName(['W', 'O', 'L', 'F', 'S', 'S', 'L', '5', '2', '0']); +pub const BORINGSSL_PUT: PutName = PutName(['B', 'O', 'R', 'I', 'N', 'G', 'S', 'S', 'L', '_']); pub const TCP_PUT: PutName = PutName(['T', 'C', 'P', '_', '_', '_', '_', '_', '_', '_']); pub const TLS_PUT_REGISTRY: PutRegistry = PutRegistry { @@ -16,6 +17,8 @@ pub const TLS_PUT_REGISTRY: PutRegistry = PutRegistry { crate::openssl::new_openssl_factory, #[cfg(feature = "wolfssl-binding")] crate::wolfssl::new_wolfssl_factory, + #[cfg(feature = "boringssl-binding")] + crate::boringssl::new_boringssl_factory, ], default: DEFAULT_PUT_FACTORY, }; @@ -26,6 +29,8 @@ pub const DEFAULT_PUT_FACTORY: fn() -> Box> = { crate::openssl::new_openssl_factory } else if #[cfg(feature = "wolfssl-binding")] { crate::wolfssl::new_wolfssl_factory + } else if #[cfg(feature = "boringssl-binding")] { + crate::boringssl::new_boringssl_factory } else { crate::tcp::new_tcp_factory } From c96340c4f1488c4b00f3d27b5f50e2be1c4843ef Mon Sep 17 00:00:00 2001 From: Tom Gouville Date: Wed, 22 Nov 2023 11:39:15 +0100 Subject: [PATCH 06/63] freezing boringssl to a specific commit --- crates/boringssl-src/src/lib.rs | 41 ++++++++++++++++++++++++-------- crates/boringssl-src/src/main.rs | 4 ++-- crates/boringssl-sys/Cargo.toml | 1 + crates/boringssl-sys/build.rs | 17 ++++++------- tlspuffin/Cargo.toml | 13 ++++++++++ 5 files changed, 56 insertions(+), 20 deletions(-) diff --git a/crates/boringssl-src/src/lib.rs b/crates/boringssl-src/src/lib.rs index 8dc7f2ca6..24a51644a 100644 --- a/crates/boringssl-src/src/lib.rs +++ b/crates/boringssl-src/src/lib.rs @@ -16,11 +16,16 @@ pub struct BoringSSLOptions { pub gcov_analysis: bool, pub llvm_cov_analysis: bool, - pub git_ref: String, + pub git_ref: GitRef, pub out_dir: PathBuf, pub source_dir: PathBuf, } +pub enum GitRef { + Branch(String), + Commit(String), +} + #[derive(Debug)] struct IgnoreMacros(HashSet); @@ -54,15 +59,31 @@ fn _patch_boringssl>( fn clone_boringssl>(dest: &P, options: &BoringSSLOptions) -> std::io::Result<()> { std::fs::remove_dir_all(dest)?; - let status = Command::new("git") - .arg("clone") - .arg("--depth") - .arg("1") - .arg("--branch") - .arg(&options.git_ref) - .arg("https://github.com/google/boringssl.git") - .arg(dest.as_ref().to_str().unwrap()) - .status()?; + let status = match &options.git_ref { + GitRef::Branch(branch_name) => Command::new("git") + .arg("clone") + .arg("--depth") + .arg("1") + .arg("--branch") + .arg(&branch_name) + .arg("https://github.com/google/boringssl.git") + .arg(dest.as_ref().to_str().unwrap()) + .status()?, + GitRef::Commit(commit_id) => { + Command::new("git") + .arg("clone") + .arg("--depth") + .arg("1") + .arg("https://github.com/google/boringssl.git") + .arg(dest.as_ref().to_str().unwrap()) + .status()?; + Command::new("git") + .current_dir(dest.as_ref().to_str().unwrap()) + .arg("checkout") + .arg(commit_id) + .status()? + } + }; if !status.success() { return Err(io::Error::from(ErrorKind::Other)); diff --git a/crates/boringssl-src/src/main.rs b/crates/boringssl-src/src/main.rs index 49a23d8a5..e3fa43989 100644 --- a/crates/boringssl-src/src/main.rs +++ b/crates/boringssl-src/src/main.rs @@ -1,6 +1,6 @@ use std::{env, path::PathBuf}; -use boringssl_src::{build, BoringSSLOptions}; +use boringssl_src::{build, BoringSSLOptions, GitRef}; fn main() { // let out_dir = PathBuf::from("out_dir"); @@ -20,7 +20,7 @@ fn main() { deterministic: false, asan: false, sancov: true, - git_ref: "master".to_string(), + git_ref: GitRef::Branch("master".to_string()), out_dir, source_dir: PathBuf::from(env::var("CARGO_MANIFEST_DIR").unwrap()).join("boringssl"), }) diff --git a/crates/boringssl-sys/Cargo.toml b/crates/boringssl-sys/Cargo.toml index f172b9112..3ab0e1da9 100644 --- a/crates/boringssl-sys/Cargo.toml +++ b/crates/boringssl-sys/Cargo.toml @@ -10,6 +10,7 @@ bindgen = "0.69.1" [features] vendored-master = [] +arbitrary-v1 = [] sancov = [] asan = [] diff --git a/crates/boringssl-sys/build.rs b/crates/boringssl-sys/build.rs index 28fc286df..f13d5cf3c 100644 --- a/crates/boringssl-sys/build.rs +++ b/crates/boringssl-sys/build.rs @@ -1,14 +1,15 @@ use std::{env, path::PathBuf}; -use boringssl_src::{build, BoringSSLOptions}; - -const REF: &str = if cfg!(feature = "vendored-master") { - "master" -} else { - "master" -}; +use boringssl_src::{build, BoringSSLOptions, GitRef}; fn main() { + let git_ref: GitRef = if cfg!(feature = "arbitrary-v1") { + GitRef::Commit(String::from("698aa894c96412d4df20e2bb031d9eb9c9d5919a")) + } else if cfg!(feature = "vendored-master") { + GitRef::Branch(String::from("master")) + } else { + GitRef::Branch(String::from("master")) + }; let source_dir = PathBuf::from(env::var("CARGO_MANIFEST_DIR").unwrap()).join("../boringssl-src/boringssl"); let out_dir = PathBuf::from(env::var("OUT_DIR").unwrap()).join("boring"); @@ -18,7 +19,7 @@ fn main() { gcov_analysis: cfg!(feature = "gcov_analysis"), llvm_cov_analysis: cfg!(feature = "llvm_cov_analysis"), deterministic: cfg!(feature = "deterministic"), - git_ref: REF.to_string(), + git_ref, out_dir: out_dir.clone(), source_dir: source_dir.clone(), }) diff --git a/tlspuffin/Cargo.toml b/tlspuffin/Cargo.toml index 4169642f4..f3475925a 100644 --- a/tlspuffin/Cargo.toml +++ b/tlspuffin/Cargo.toml @@ -178,6 +178,19 @@ boringsslmaster = [ "deterministic", ] +boringsslv1 = [ + "boringssl-sys/arbitrary-v1", + "tls12", + "tls13", + "boringssl-binding", + "tls12-session-resumption", + "tls13-session-resumption", + "transcript-extraction", + "client-authentication-transcript-extraction", + "deterministic", +] + + # Logs each execution of __sanitizer_cov_trace_pc_guard* sancov_pcguard_log = ["puffin/sancov_pcguard_log"] # Uses libafl for the instrumentation. sancov_pcguard_log and sancov_libafl are mutally exclusive From 4f97f9dc80633daa488032526da89ff81326d696 Mon Sep 17 00:00:00 2001 From: Tom Gouville Date: Wed, 22 Nov 2023 15:50:03 +0100 Subject: [PATCH 07/63] fixing regression while building boringssl --- crates/boringssl-src/src/lib.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/crates/boringssl-src/src/lib.rs b/crates/boringssl-src/src/lib.rs index 24a51644a..fd1251e32 100644 --- a/crates/boringssl-src/src/lib.rs +++ b/crates/boringssl-src/src/lib.rs @@ -144,8 +144,12 @@ fn build_boringssl>(dest: &P, options: &BoringSSLOptions) -> Path boring_conf .cflag("-fsanitize=address") .cflag("-shared-libsan") + .cflag("-Wno-unused-command-line-argument") + .cflag(format!("-Wl,-rpath={}/lib/linux/", clang)) .cxxflag("-fsanitize=address") .cxxflag("-shared-libsan") + .cxxflag("-Wno-unused-command-line-argument") + .cxxflag(format!("-Wl,-rpath={}/lib/linux/", clang)) .define("OPENSSL_NO_BUF_FREELISTS", "1") .define("OPENSSL_NO_ASM", "1"); } From f487162bb1e8ea177ca650236ecf01ff8d0fab0a Mon Sep 17 00:00:00 2001 From: Tom Gouville Date: Wed, 22 Nov 2023 16:47:53 +0100 Subject: [PATCH 08/63] changing source directory for boringssl --- crates/boringssl-src/boringssl/.gitkeep | 0 crates/boringssl-src/src/lib.rs | 2 +- crates/boringssl-sys/build.rs | 3 +-- 3 files changed, 2 insertions(+), 3 deletions(-) delete mode 100644 crates/boringssl-src/boringssl/.gitkeep diff --git a/crates/boringssl-src/boringssl/.gitkeep b/crates/boringssl-src/boringssl/.gitkeep deleted file mode 100644 index e69de29bb..000000000 diff --git a/crates/boringssl-src/src/lib.rs b/crates/boringssl-src/src/lib.rs index fd1251e32..400851d47 100644 --- a/crates/boringssl-src/src/lib.rs +++ b/crates/boringssl-src/src/lib.rs @@ -58,7 +58,7 @@ fn _patch_boringssl>( } fn clone_boringssl>(dest: &P, options: &BoringSSLOptions) -> std::io::Result<()> { - std::fs::remove_dir_all(dest)?; + std::fs::remove_dir_all(dest).unwrap_or(()); let status = match &options.git_ref { GitRef::Branch(branch_name) => Command::new("git") .arg("clone") diff --git a/crates/boringssl-sys/build.rs b/crates/boringssl-sys/build.rs index f13d5cf3c..000132409 100644 --- a/crates/boringssl-sys/build.rs +++ b/crates/boringssl-sys/build.rs @@ -10,8 +10,7 @@ fn main() { } else { GitRef::Branch(String::from("master")) }; - let source_dir = - PathBuf::from(env::var("CARGO_MANIFEST_DIR").unwrap()).join("../boringssl-src/boringssl"); + let source_dir = PathBuf::from(env::var("OUT_DIR").unwrap()).join("boringssl"); let out_dir = PathBuf::from(env::var("OUT_DIR").unwrap()).join("boring"); build(&BoringSSLOptions { asan: cfg!(feature = "asan"), From 182feb3ebcb5c67ea5a4ea6d708b21a9061c9df0 Mon Sep 17 00:00:00 2001 From: Tom Gouville Date: Fri, 24 Nov 2023 13:45:06 +0100 Subject: [PATCH 09/63] fix problem while cloning boringssl repo --- crates/boringssl-src/src/lib.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/crates/boringssl-src/src/lib.rs b/crates/boringssl-src/src/lib.rs index 400851d47..d5046a832 100644 --- a/crates/boringssl-src/src/lib.rs +++ b/crates/boringssl-src/src/lib.rs @@ -72,8 +72,6 @@ fn clone_boringssl>(dest: &P, options: &BoringSSLOptions) -> std: GitRef::Commit(commit_id) => { Command::new("git") .arg("clone") - .arg("--depth") - .arg("1") .arg("https://github.com/google/boringssl.git") .arg(dest.as_ref().to_str().unwrap()) .status()?; From 822114c255b2906efadcb6b0c5d606c9e7111dcd Mon Sep 17 00:00:00 2001 From: HIRSCHI Lucca Date: Thu, 14 Dec 2023 15:20:43 +0100 Subject: [PATCH 10/63] openssl deterministic test fails --- puffin/src/agent.rs | 19 +++++++++++++++++ puffin/src/algebra/error.rs | 2 +- puffin/src/algebra/mod.rs | 1 + puffin/src/claims.rs | 4 ++-- puffin/src/error.rs | 2 +- puffin/src/put_registry.rs | 10 +++++++++ puffin/src/trace.rs | 17 ++++++++++++++- sshpuffin/src/protocol.rs | 1 + tlspuffin/src/openssl/deterministic.rs | 29 ++++++++++++++++++++++++-- tlspuffin/src/protocol.rs | 2 +- 10 files changed, 79 insertions(+), 8 deletions(-) diff --git a/puffin/src/agent.rs b/puffin/src/agent.rs index a0452874a..7750f4425 100644 --- a/puffin/src/agent.rs +++ b/puffin/src/agent.rs @@ -5,6 +5,7 @@ //! Each [`Agent`] has an *inbound* and an *outbound channel* (see [`crate::io`]) use core::fmt; +use std::fmt::{Debug, Formatter}; use serde::{Deserialize, Serialize}; @@ -145,6 +146,24 @@ pub struct Agent { put_descriptor: PutDescriptor, } +impl Debug for Agent { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + f.debug_struct("Agent") + .field("name", &self.name) + .field("put", &self.put.describe_state()) + .field("put_descriptor", &self.put_descriptor) + .finish() + } +} + +impl PartialEq for Agent { + fn eq(&self, other: &Self) -> bool { + self.name.eq(&other.name) && + self.put.describe_state() == other.put.describe_state() && + self.put_descriptor.eq(&other.put_descriptor) + } +} + impl Agent { pub fn new( context: &TraceContext, diff --git a/puffin/src/algebra/error.rs b/puffin/src/algebra/error.rs index 6b3370d1d..0e051298d 100644 --- a/puffin/src/algebra/error.rs +++ b/puffin/src/algebra/error.rs @@ -2,7 +2,7 @@ use std::fmt; use serde::{Deserialize, Serialize}; -#[derive(Debug, Clone, Serialize, Deserialize)] +#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] pub enum FnError { Unknown(String), /// Error which happened because a cryptographic operation failed. diff --git a/puffin/src/algebra/mod.rs b/puffin/src/algebra/mod.rs index b06975748..fe4f09028 100644 --- a/puffin/src/algebra/mod.rs +++ b/puffin/src/algebra/mod.rs @@ -498,6 +498,7 @@ pub mod test_signature { } } + #[derive(Debug, PartialEq)] pub struct TestProtocolBehavior; impl ProtocolBehavior for TestProtocolBehavior { diff --git a/puffin/src/claims.rs b/puffin/src/claims.rs index cde9123c4..7812c9698 100644 --- a/puffin/src/claims.rs +++ b/puffin/src/claims.rs @@ -22,7 +22,7 @@ pub trait SecurityViolationPolicy { fn check_violation(claims: &[C]) -> Option<&'static str>; } -#[derive(Clone, Debug)] +#[derive(Clone, Debug, PartialEq)] pub struct ClaimList { claims: Vec, } @@ -82,7 +82,7 @@ impl ClaimList { } } -#[derive(Clone)] +#[derive(Clone,PartialEq, Debug)] pub struct GlobalClaimList { claims: Rc>>, } diff --git a/puffin/src/error.rs b/puffin/src/error.rs index 98efaa268..402a0b0a4 100644 --- a/puffin/src/error.rs +++ b/puffin/src/error.rs @@ -2,7 +2,7 @@ use std::{fmt, fmt::Formatter, io}; use crate::algebra::error::FnError; -#[derive(Debug, Clone)] +#[derive(Debug, Clone, PartialEq)] pub enum Error { /// Returned if a concrete function from the module [`tls`] fails or term evaluation fails Fn(FnError), diff --git a/puffin/src/put_registry.rs b/puffin/src/put_registry.rs index e0c2cb03b..3b48ee427 100644 --- a/puffin/src/put_registry.rs +++ b/puffin/src/put_registry.rs @@ -1,3 +1,4 @@ +use std::fmt::{Debug, Formatter}; use crate::{ agent::AgentDescriptor, error::Error, @@ -10,11 +11,20 @@ pub const DUMMY_PUT: PutName = PutName(['D', 'U', 'M', 'Y', 'Y', 'D', 'U', 'M', /// Registry for [Factories](Factory). An instance of this is usually defined statically and then /// used throughout the fuzzer. +#[derive(PartialEq)] pub struct PutRegistry { pub factories: &'static [fn() -> Box>], pub default: fn() -> Box>, } +impl Debug for PutRegistry { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + f.debug_struct("PutRegistry (default only)") + .field("default", &(self.default)().name()) + .finish() + } +} + impl PutRegistry { pub fn version_strings(&self) -> Vec { let mut put_versions = Vec::new(); diff --git a/puffin/src/trace.rs b/puffin/src/trace.rs index 8cde203d6..0bdd0825b 100644 --- a/puffin/src/trace.rs +++ b/puffin/src/trace.rs @@ -64,6 +64,7 @@ impl Knowledge { /// [Knowledge] describes an atomic piece of knowledge inferred /// by the [`crate::variable_data::extract_knowledge`] function /// [Knowledge] is made of the data, the agent that produced the output, the TLS message type and the internal type. +#[derive(Debug)] pub struct Knowledge { pub agent_name: AgentName, pub matcher: Option, @@ -96,11 +97,12 @@ impl fmt::Display for Knowledge { /// client and server extensions, cipher suits or session ID It also holds the concrete /// references to the [`Agent`]s and the underlying streams, which contain the messages /// which have need exchanged and are not yet processed by an output step. +#[derive(Debug)] pub struct TraceContext { /// The knowledge of the attacker knowledge: Vec>, agents: Vec>, - claims: GlobalClaimList, + claims: GlobalClaimList<::Claim>, put_registry: &'static PutRegistry, deterministic_put: bool, @@ -110,6 +112,19 @@ pub struct TraceContext { phantom: PhantomData, } + +impl PartialEq for TraceContext { + fn eq(&self, other: &Self) -> bool { + self.agents == other.agents && + self.put_registry == other.put_registry && + self.deterministic_put == other.deterministic_put && + self.default_put_options == other.default_put_options && + self.non_default_put_descriptors == other.non_default_put_descriptors && + format!("{:?}", self.knowledge) == format!("{:?}", other.knowledge) && + format!("{:?}", self.claims) == format!("{:?}", other.claims) + } +} + impl TraceContext { pub fn new(put_registry: &'static PutRegistry, default_put_options: PutOptions) -> Self { // We keep a global list of all claims throughout the execution. Each claim is identified diff --git a/sshpuffin/src/protocol.rs b/sshpuffin/src/protocol.rs index 8ee3d5cba..f5fabe8ed 100644 --- a/sshpuffin/src/protocol.rs +++ b/sshpuffin/src/protocol.rs @@ -28,6 +28,7 @@ use crate::{ #[derive(Clone)] pub struct SshProtocolBehavior {} +#[derive(Debug, PartialEq)] impl ProtocolBehavior for SshProtocolBehavior { type Claim = SshClaim; type SecurityViolationPolicy = SshSecurityViolationPolicy; diff --git a/tlspuffin/src/openssl/deterministic.rs b/tlspuffin/src/openssl/deterministic.rs index 9395f4a78..a6023f9ba 100644 --- a/tlspuffin/src/openssl/deterministic.rs +++ b/tlspuffin/src/openssl/deterministic.rs @@ -21,15 +21,40 @@ pub fn set_openssl_deterministic() { #[cfg(test)] mod tests { + use std::fmt::format; use openssl::rand::rand_bytes; - + use crate::tls::seeds::{create_corpus, seed_client_attacker_full}; + use puffin::trace::{Action, InputAction, OutputAction, Step, Trace, TraceContext}; + use crate::put_registry::TLS_PUT_REGISTRY; + use puffin::put::PutOptions; + use crate::tls::{ + trace_helper::TraceHelper, + }; #[test] #[cfg(feature = "openssl111-binding")] - fn test_openssl_no_randomness() { + fn test_openssl_no_randomness_simple() { use crate::openssl::deterministic::set_openssl_deterministic; set_openssl_deterministic(); let mut buf1 = [0; 2]; rand_bytes(&mut buf1).unwrap(); assert_eq!(buf1, [70, 100]); } + + #[test] + fn test_openssl_no_randomness_full() { + use crate::openssl::deterministic::set_openssl_deterministic; + set_openssl_deterministic(); + + let trace = seed_client_attacker_full.build_trace(); + let mut ctx1 = TraceContext::new(&TLS_PUT_REGISTRY, PutOptions::default()); + ctx1.set_deterministic(true); + trace.execute(&mut ctx1); + let mut ctx2 = TraceContext::new(&TLS_PUT_REGISTRY, PutOptions::default()); + ctx2.set_deterministic(true); + trace.execute(&mut ctx2); + + println!("Left: {:#?}\n", ctx1); + println!("Right: {:#?}\n", ctx2); + assert_eq!(ctx1, ctx2); + } } diff --git a/tlspuffin/src/protocol.rs b/tlspuffin/src/protocol.rs index 252d38eed..6d87e0c12 100644 --- a/tlspuffin/src/protocol.rs +++ b/tlspuffin/src/protocol.rs @@ -181,7 +181,7 @@ impl Matcher for msgs::enums::HandshakeType { } } -#[derive(Clone)] +#[derive(Clone, Debug, PartialEq)] pub struct TLSProtocolBehavior; impl ProtocolBehavior for TLSProtocolBehavior { From 1d42a4fdbc5818224053aa7707e5a956aa1c5b73 Mon Sep 17 00:00:00 2001 From: HIRSCHI Lucca Date: Thu, 14 Dec 2023 15:39:25 +0100 Subject: [PATCH 11/63] openssl deterministic test fails --- puffin/src/agent.rs | 10 +++++----- puffin/src/claims.rs | 2 +- puffin/src/put_registry.rs | 2 +- puffin/src/trace.rs | 17 ++++++++--------- tlspuffin/src/openssl/deterministic.rs | 12 +++++------- 5 files changed, 20 insertions(+), 23 deletions(-) diff --git a/puffin/src/agent.rs b/puffin/src/agent.rs index 7750f4425..c306e916e 100644 --- a/puffin/src/agent.rs +++ b/puffin/src/agent.rs @@ -146,7 +146,7 @@ pub struct Agent { put_descriptor: PutDescriptor, } -impl Debug for Agent { +impl Debug for Agent { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { f.debug_struct("Agent") .field("name", &self.name) @@ -156,11 +156,11 @@ impl Debug for Agent { } } -impl PartialEq for Agent { +impl PartialEq for Agent { fn eq(&self, other: &Self) -> bool { - self.name.eq(&other.name) && - self.put.describe_state() == other.put.describe_state() && - self.put_descriptor.eq(&other.put_descriptor) + self.name.eq(&other.name) + && self.put.describe_state() == other.put.describe_state() + && self.put_descriptor.eq(&other.put_descriptor) } } diff --git a/puffin/src/claims.rs b/puffin/src/claims.rs index 7812c9698..4849e4fff 100644 --- a/puffin/src/claims.rs +++ b/puffin/src/claims.rs @@ -82,7 +82,7 @@ impl ClaimList { } } -#[derive(Clone,PartialEq, Debug)] +#[derive(Clone, PartialEq, Debug)] pub struct GlobalClaimList { claims: Rc>>, } diff --git a/puffin/src/put_registry.rs b/puffin/src/put_registry.rs index 3b48ee427..c19fe6444 100644 --- a/puffin/src/put_registry.rs +++ b/puffin/src/put_registry.rs @@ -1,4 +1,3 @@ -use std::fmt::{Debug, Formatter}; use crate::{ agent::AgentDescriptor, error::Error, @@ -6,6 +5,7 @@ use crate::{ put::{Put, PutName}, trace::TraceContext, }; +use std::fmt::{Debug, Formatter}; pub const DUMMY_PUT: PutName = PutName(['D', 'U', 'M', 'Y', 'Y', 'D', 'U', 'M', 'M', 'Y']); diff --git a/puffin/src/trace.rs b/puffin/src/trace.rs index 0bdd0825b..73f23624f 100644 --- a/puffin/src/trace.rs +++ b/puffin/src/trace.rs @@ -112,16 +112,15 @@ pub struct TraceContext { phantom: PhantomData, } - -impl PartialEq for TraceContext { +impl PartialEq for TraceContext { fn eq(&self, other: &Self) -> bool { - self.agents == other.agents && - self.put_registry == other.put_registry && - self.deterministic_put == other.deterministic_put && - self.default_put_options == other.default_put_options && - self.non_default_put_descriptors == other.non_default_put_descriptors && - format!("{:?}", self.knowledge) == format!("{:?}", other.knowledge) && - format!("{:?}", self.claims) == format!("{:?}", other.claims) + self.agents == other.agents + && self.put_registry == other.put_registry + && self.deterministic_put == other.deterministic_put + && self.default_put_options == other.default_put_options + && self.non_default_put_descriptors == other.non_default_put_descriptors + && format!("{:?}", self.knowledge) == format!("{:?}", other.knowledge) + && format!("{:?}", self.claims) == format!("{:?}", other.claims) } } diff --git a/tlspuffin/src/openssl/deterministic.rs b/tlspuffin/src/openssl/deterministic.rs index a6023f9ba..f798af19a 100644 --- a/tlspuffin/src/openssl/deterministic.rs +++ b/tlspuffin/src/openssl/deterministic.rs @@ -21,15 +21,13 @@ pub fn set_openssl_deterministic() { #[cfg(test)] mod tests { - use std::fmt::format; - use openssl::rand::rand_bytes; - use crate::tls::seeds::{create_corpus, seed_client_attacker_full}; - use puffin::trace::{Action, InputAction, OutputAction, Step, Trace, TraceContext}; use crate::put_registry::TLS_PUT_REGISTRY; + use crate::tls::seeds::{create_corpus, seed_client_attacker_full}; + use crate::tls::trace_helper::TraceHelper; + use openssl::rand::rand_bytes; use puffin::put::PutOptions; - use crate::tls::{ - trace_helper::TraceHelper, - }; + use puffin::trace::{Action, InputAction, OutputAction, Step, Trace, TraceContext}; + use std::fmt::format; #[test] #[cfg(feature = "openssl111-binding")] fn test_openssl_no_randomness_simple() { From 57366a0394c90d4cb97fd60fd656308fe1fd7fb8 Mon Sep 17 00:00:00 2001 From: Tom Gouville Date: Fri, 15 Dec 2023 13:48:41 +0100 Subject: [PATCH 12/63] adding client_attacker_boring trace which performs a handshake with boringssl --- tlspuffin/src/boringssl/mod.rs | 22 --- tlspuffin/src/tls/fn_utils.rs | 87 +++++++++++ tlspuffin/src/tls/mod.rs | 5 + tlspuffin/src/tls/rustls/msgs/message.rs | 37 +++++ tlspuffin/src/tls/seeds.rs | 188 +++++++++++++++++++++++ 5 files changed, 317 insertions(+), 22 deletions(-) diff --git a/tlspuffin/src/boringssl/mod.rs b/tlspuffin/src/boringssl/mod.rs index d28896284..fec66c27e 100644 --- a/tlspuffin/src/boringssl/mod.rs +++ b/tlspuffin/src/boringssl/mod.rs @@ -379,23 +379,8 @@ impl BoringSSL { ctx_builder.set_verify(SslVerifyMode::NONE); } - // #[cfg(feature = "openssl111-binding")] - // ctx_builder.clear_options(boring::ssl::SslOptions::ENABLE_MIDDLEBOX_COMPAT); - - // #[cfg(feature = "openssl111-binding")] - // bindings::set_allow_no_dhe_kex(&mut ctx_builder); - set_max_protocol_version(&mut ctx_builder, descriptor.tls_version)?; - #[cfg(any(feature = "openssl101-binding", feature = "openssl102-binding"))] - { - ctx_builder.set_tmp_ecdh( - &openssl::ec::EcKey::from_curve_name(openssl::nid::Nid::SECP384R1)?.as_ref(), - )?; - - bindings::set_tmp_rsa(&ctx_builder, &openssl::rsa::Rsa::generate(512)?)?; - } - // Allow EXPORT in server ctx_builder.set_cipher_list("ALL:EXPORT:!LOW:!aNULL:!eNULL:!SSLv2")?; @@ -407,13 +392,6 @@ impl BoringSSL { fn create_client(descriptor: &AgentDescriptor) -> Result { let mut ctx_builder = SslContext::builder(SslMethod::tls())?; - // Not sure whether we want this disabled or enabled: https://github.com/tlspuffin/tlspuffin/issues/67 - // The tests become simpler if disabled to maybe that's what we want. Lets leave it default - // for now. - // https://wiki.openssl.org/index.php/TLS1.3#Middlebox_Compatibility_Mode - #[cfg(feature = "openssl111-binding")] - ctx_builder.clear_options(openssl::ssl::SslOptions::ENABLE_MIDDLEBOX_COMPAT); - set_max_protocol_version(&mut ctx_builder, descriptor.tls_version)?; // Disallow EXPORT in client diff --git a/tlspuffin/src/tls/fn_utils.rs b/tlspuffin/src/tls/fn_utils.rs index fa059785a..03602f9ed 100644 --- a/tlspuffin/src/tls/fn_utils.rs +++ b/tlspuffin/src/tls/fn_utils.rs @@ -79,6 +79,93 @@ pub fn fn_decrypt_handshake( .map_err(|_err| FnError::Crypto("Failed to create Message from decrypted data".to_string())) } +/// Decrypt an Application data message containing multiple handshake messages +/// and return a vec of handshake messages +pub fn fn_decrypt_multiple_handshake_messages( + application_data: &Message, + server_hello_transcript: &HandshakeHash, + server_key_share: &Option>, + psk: &Option>, + group: &NamedGroup, + client: &bool, + sequence: &u64, +) -> Result, FnError> { + let (suite, key, _) = tls13_handshake_traffic_secret( + server_hello_transcript, + server_key_share, + psk, + !*client, + group, + )?; + let decrypter = suite + .tls13() + .ok_or_else(|| FnError::Crypto("No tls 1.3 suite".to_owned()))? + .derive_decrypter(&key); + let message = decrypter + .decrypt( + PlainMessage::from(application_data.clone()).into_unencrypted_opaque(), + *sequence, + ) + .map_err(|_err| FnError::Crypto("Failed to decrypt it fn_decrypt_handshake".to_string()))?; + + let payloads = + MessagePayload::multiple_new(message.typ, message.version, message.payload).unwrap(); + + let messages = payloads + .into_iter() + .map(|p| Message { + version: message.version, + payload: p, + }) + .collect(); + + Ok(messages) +} + +pub fn fn_find_server_certificate(messages: &Vec) -> Result { + for msg in messages { + if let MessagePayload::Handshake(x) = &msg.payload { + if x.typ == HandshakeType::Certificate { + return Ok(msg.clone()); + } + } + } + Err(FnError::Unknown("no server certificate".to_owned())) +} + +pub fn fn_find_encrypted_extensions(messages: &Vec) -> Result { + for msg in messages { + if let MessagePayload::Handshake(x) = &msg.payload { + if x.typ == HandshakeType::EncryptedExtensions { + return Ok(msg.clone()); + } + } + } + Err(FnError::Unknown("no encrypted extensions".to_owned())) +} + +pub fn fn_find_server_certificate_verify(messages: &Vec) -> Result { + for msg in messages { + if let MessagePayload::Handshake(x) = &msg.payload { + if x.typ == HandshakeType::CertificateVerify { + return Ok(msg.clone()); + } + } + } + Err(FnError::Unknown("no certificate verify".to_owned())) +} + +pub fn fn_find_server_finished(messages: &Vec) -> Result { + for msg in messages { + if let MessagePayload::Handshake(x) = &msg.payload { + if x.typ == HandshakeType::Finished { + return Ok(msg.clone()); + } + } + } + Err(FnError::Unknown("no finished".to_owned())) +} + pub fn fn_no_psk() -> Result>, FnError> { Ok(None) } diff --git a/tlspuffin/src/tls/mod.rs b/tlspuffin/src/tls/mod.rs index c4d4a1400..0d685d868 100644 --- a/tlspuffin/src/tls/mod.rs +++ b/tlspuffin/src/tls/mod.rs @@ -206,6 +206,11 @@ define_signature!( fn_new_transcript fn_append_transcript fn_decrypt_handshake + fn_decrypt_multiple_handshake_messages + fn_find_server_certificate + fn_find_server_certificate_verify + fn_find_encrypted_extensions + fn_find_server_finished fn_no_psk fn_psk fn_decrypt_application diff --git a/tlspuffin/src/tls/rustls/msgs/message.rs b/tlspuffin/src/tls/rustls/msgs/message.rs index 07d98776a..e48bd49d9 100644 --- a/tlspuffin/src/tls/rustls/msgs/message.rs +++ b/tlspuffin/src/tls/rustls/msgs/message.rs @@ -64,6 +64,43 @@ impl MessagePayload { .ok_or_else(|| Error::corrupt_message(typ))*/ } + /// Extract multiple messages payloads from one ApplicationData message + pub fn multiple_new( + typ: ContentType, + vers: ProtocolVersion, + payload: Payload, + ) -> Result, Error> { + let fallback_payload = payload.clone(); + let mut r = Reader::init(&payload.0); + let mut parsed: Vec = vec![]; + while r.any_left() { + let parsed_msg = match typ { + ContentType::ApplicationData => Some(Self::ApplicationData(payload.clone())), + ContentType::Alert => AlertMessagePayload::read(&mut r).map(MessagePayload::Alert), + ContentType::Handshake => { + HandshakeMessagePayload::read_version(&mut r, vers) + .map(MessagePayload::Handshake) + // this type is for TLS 1.2 encrypted handshake messages + .or(Some(MessagePayload::TLS12EncryptedHandshake( + fallback_payload.clone(), + ))) + } + ContentType::ChangeCipherSpec => { + ChangeCipherSpecPayload::read(&mut r).map(MessagePayload::ChangeCipherSpec) + } + ContentType::Heartbeat => { + HeartbeatPayload::read(&mut r).map(MessagePayload::Heartbeat) + } + _ => None, + }; + if let Some(msg) = parsed_msg { + parsed.push(msg); + } + } + + Ok(parsed) + } + pub fn content_type(&self) -> ContentType { match self { Self::Alert(_) => ContentType::Alert, diff --git a/tlspuffin/src/tls/seeds.rs b/tlspuffin/src/tls/seeds.rs index 04dab84ff..983592fe7 100644 --- a/tlspuffin/src/tls/seeds.rs +++ b/tlspuffin/src/tls/seeds.rs @@ -21,6 +21,8 @@ use crate::{ }, }; +use super::rustls::msgs::handshake::EncryptedExtensions; + pub fn seed_successful_client_auth(client: AgentName, server: AgentName) -> Trace { Trace { prior_traces: vec![], @@ -1603,6 +1605,190 @@ pub fn _seed_client_attacker_full( ) } +pub fn seed_client_attacker_boring(server: AgentName) -> Trace { + _seed_client_attacker_boring(server).0 +} + +/// Seed which contains the whole transcript in the tree. This is rather huge >300 symbols +pub fn _seed_client_attacker_boring( + server: AgentName, +) -> ( + Trace, + Term, + Term, + Term, +) { + let client_hello = term! { + fn_client_hello( + fn_protocol_version12, + fn_new_random, + fn_new_session_id, + (fn_append_cipher_suite( + (fn_new_cipher_suites()), + fn_cipher_suite13_aes_128_gcm_sha256 + )), + fn_compressions, + (fn_client_extensions_append( + (fn_client_extensions_append( + (fn_client_extensions_append( + (fn_client_extensions_append( + fn_client_extensions_new, + (fn_support_group_extension(fn_named_group_secp384r1)) + )), + fn_signature_algorithm_extension + )), + (fn_key_share_deterministic_extension(fn_named_group_secp384r1)) + )), + fn_supported_versions13_extension + )) + ) + }; + + let server_hello_transcript = term! { + fn_append_transcript( + (fn_append_transcript( + fn_new_transcript, + (@client_hello) // ClientHello + )), + ((server, 0)[Some(TlsQueryMatcher::Handshake(Some(HandshakeType::ServerHello)))]) // plaintext ServerHello + ) + }; + + // ((0, 1)) could be a CCS the server sends one + + let extensions = term! { + fn_decrypt_multiple_handshake_messages( + ((server, 0)[Some(TlsQueryMatcher::ApplicationData)]), // Encrypted Extensions + (@server_hello_transcript), + (fn_get_server_key_share(((server, 0)[Some(TlsQueryMatcher::Handshake(Some(HandshakeType::ServerHello)))]))), + fn_no_psk, + fn_named_group_secp384r1, + fn_true, + fn_seq_0 // sequence 0 + ) + }; + + let encrypted_extensions = term! { + fn_find_encrypted_extensions((@extensions)) + }; + + let encrypted_extension_transcript = term! { + fn_append_transcript( + (@server_hello_transcript), + (@encrypted_extensions) // plaintext Encrypted Extensions + ) + }; + + let server_certificate = term! { + fn_find_server_certificate((@extensions)) + }; + + let server_certificate_transcript = term! { + fn_append_transcript( + (@encrypted_extension_transcript), + (@server_certificate) // plaintext Server Certificate + ) + }; + + let server_certificate_verify = term! { + fn_find_server_certificate_verify((@extensions)) + }; + + let server_certificate_verify_transcript = term! { + fn_append_transcript( + (@server_certificate_transcript), + (@server_certificate_verify) // plaintext Server Certificate Verify + ) + }; + + let server_finished = term! { + fn_find_server_finished((@extensions)) + }; + + let server_finished_transcript = term! { + fn_append_transcript( + (@server_certificate_verify_transcript), + (@server_finished) // plaintext Server Handshake Finished + ) + }; + + let client_finished = term! { + fn_finished( + (fn_verify_data( + (@server_finished_transcript), + (@server_hello_transcript), + (fn_get_server_key_share(((server, 0)))), + fn_no_psk, + fn_named_group_secp384r1 + )) + ) + }; + + let client_finished_transcript = term! { + fn_append_transcript( + (@server_finished_transcript), + (@client_finished) + ) + }; + + let trace = Trace { + prior_traces: vec![], + descriptors: vec![AgentDescriptor::new_server(server, TLSVersion::V1_3)], + steps: vec![ + Step { + agent: server, + action: Action::Input(InputAction { + recipe: term! { + @client_hello + }, + }), + }, + OutputAction::new_step(server), + Step { + agent: server, + action: Action::Input(InputAction { + recipe: term! { + fn_encrypt_handshake( + (@client_finished), + (@server_hello_transcript), + (fn_get_server_key_share(((server, 0)))), + fn_no_psk, + fn_named_group_secp384r1, + fn_true, + fn_seq_0 // sequence 0 + ) + }, + }), + }, + OutputAction::new_step(server), + /*Step { + agent: server, + action: Action::Input(InputAction { + recipe: term! { + fn_encrypt_application( + fn_alert_close_notify, + (@server_hello_transcript), + (@server_finished_transcript), + (fn_get_server_key_share(((server, 0)))), + fn_no_psk, + fn_named_group_secp384r1, + fn_seq_0 // sequence 0 + ) + }, + }), + }, + OutputAction::new_step(server),*/ + ], + }; + + ( + trace, + server_hello_transcript, + server_finished_transcript, + client_finished_transcript, + ) +} + /// Seed which contains the whole transcript in the tree. This is rather huge 10k symbols. It grows /// exponentially. pub fn seed_session_resumption_dhe_full( @@ -1822,6 +2008,8 @@ pub fn create_corpus() -> Vec<(Trace, &'static str)> { seed_successful12_with_tickets: cfg(feature = "tls12-session-resumption"), // Client Attackers seed_client_attacker: cfg(feature = "tls13"), + seed_client_attacker_full: cfg(feature = "tls13"), + seed_client_attacker_boring: cfg(feature = "tls13"), seed_client_attacker_auth: cfg(all(feature = "tls13", feature = "client-authentication-transcript-extraction")), seed_client_attacker12: cfg(feature = "tls12"), // Session resumption From aa340e58029748293e4d9c92a357460d2ab97d04 Mon Sep 17 00:00:00 2001 From: HIRSCHI Lucca Date: Wed, 20 Dec 2023 16:45:15 +0100 Subject: [PATCH 13/63] New determinism architecture: - distinguish re-set RAND from reseed - reset RAND once for all for all factories with `determinism_set_reseed_all_factories` - reseed all factories before executing a trace with `determinism_reseed_all_factories` --> New integration test for determinsim. Passes for OpenSSL! --- .../openssl-src-111/src/deterministic_rand.c | 11 ++-- puffin/src/claims.rs | 2 +- puffin/src/put.rs | 3 +- puffin/src/put_registry.rs | 21 ++++++++ puffin/src/trace.rs | 17 ++++++- sshpuffin/src/libssh/mod.rs | 2 +- .../src/integration_tests/determinism.rs | 50 +++++++++++++++++++ tlspuffin/src/integration_tests/mod.rs | 2 + tlspuffin/src/openssl/deterministic.rs | 43 ++++++---------- tlspuffin/src/openssl/mod.rs | 17 +++++-- tlspuffin/src/tcp/mod.rs | 18 +++++-- tlspuffin/src/wolfssl/mod.rs | 4 +- 12 files changed, 143 insertions(+), 47 deletions(-) create mode 100644 tlspuffin/src/integration_tests/determinism.rs diff --git a/crates/openssl-src-111/src/deterministic_rand.c b/crates/openssl-src-111/src/deterministic_rand.c index f4b8f7804..842fde7ab 100644 --- a/crates/openssl-src-111/src/deterministic_rand.c +++ b/crates/openssl-src-111/src/deterministic_rand.c @@ -2,6 +2,11 @@ #include #include +unsigned int seed = 0; +unsigned int m = 0xFFFFFFFF; +unsigned int a = 22695477; +unsigned int c = 1; + #define UNUSED(x) (void)(x) // Seed the RNG. srand() takes an unsigned int, so we just use the first @@ -10,10 +15,9 @@ static int stdlib_rand_seed(const void *buf, int num) { if (num < 1) { - srand(0); return 0; } - srand(*((unsigned int *) buf)); + seed = *((unsigned int *) buf); return 1; } @@ -23,7 +27,8 @@ static int stdlib_rand_bytes(unsigned char *buf, int num) { for (int index = 0; index < num; ++index) { - buf[index] = rand() % 256; + seed = (a * seed + c) % m; + buf[index] = seed % 256; } return 1; } diff --git a/puffin/src/claims.rs b/puffin/src/claims.rs index 4849e4fff..c1076cf70 100644 --- a/puffin/src/claims.rs +++ b/puffin/src/claims.rs @@ -12,7 +12,7 @@ use log::{debug, trace}; use crate::{agent::AgentName, algebra::dynamic_function::TypeShape, variable_data::VariableData}; -pub trait Claim: VariableData { +pub trait Claim: VariableData + Debug { fn agent_name(&self) -> AgentName; fn id(&self) -> TypeShape; fn inner(&self) -> Box; diff --git a/puffin/src/put.rs b/puffin/src/put.rs index fc716d7e4..3b493d17b 100644 --- a/puffin/src/put.rs +++ b/puffin/src/put.rs @@ -100,7 +100,8 @@ pub trait Put: fn is_state_successful(&self) -> bool; /// Make the PUT used by self determimistic in the future by making its PRNG "deterministic" - fn set_deterministic(&mut self) -> Result<(), Error>; + /// Now subsumed by Factory-level functions to reseed globally: `determinism_reseed` + fn determinism_reseed(&mut self) -> Result<(), Error>; /// checks whether a agent is reusable with the descriptor fn is_reusable_with(&self, other: &AgentDescriptor) -> bool { diff --git a/puffin/src/put_registry.rs b/puffin/src/put_registry.rs index c19fe6444..273152951 100644 --- a/puffin/src/put_registry.rs +++ b/puffin/src/put_registry.rs @@ -6,6 +6,7 @@ use crate::{ trace::TraceContext, }; use std::fmt::{Debug, Formatter}; +use log::debug; pub const DUMMY_PUT: PutName = PutName(['D', 'U', 'M', 'Y', 'Y', 'D', 'U', 'M', 'M', 'Y']); @@ -48,6 +49,21 @@ impl PutRegistry { .map(|func| func()) .find(|factory: &Box>| factory.name() == put_name) } + + /// To be called at the beginning of all fuzzing campaigns! + pub fn determinism_set_reseed_all_factories(&self) -> () { + debug!("== Set and reseed all ({}):", self.factories.len()); + for func in self.factories { + func().determinism_set_reseed(); + } + } + + pub fn determinism_reseed_all_factories(&self) -> () { + debug!("== Reseed all ({}):", self.factories.len()); + for func in self.factories { + func().determinism_reseed(); + } + } } /// Factory for instantiating programs-under-test. @@ -59,4 +75,9 @@ pub trait Factory { ) -> Result>, Error>; fn name(&self) -> PutName; fn version(&self) -> String; + + fn determinism_set_reseed(&self) -> (); + + fn determinism_reseed(&self) -> (); + } diff --git a/puffin/src/trace.rs b/puffin/src/trace.rs index 73f23624f..56c262a68 100644 --- a/puffin/src/trace.rs +++ b/puffin/src/trace.rs @@ -17,7 +17,7 @@ use core::fmt; use std::{ any::{Any, TypeId}, collections::HashMap, - fmt::Debug, + fmt::{Display,Debug}, hash::Hash, marker::PhantomData, }; @@ -112,6 +112,16 @@ pub struct TraceContext { phantom: PhantomData, } +impl fmt::Display for TraceContext { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "Knowledge [not displaying other fields] (size={}):", self.knowledge.len()); + for k in &self.knowledge { + write!(f, "\n {}, -- {:?}", k, k); + } + Ok(()) + } +} + impl PartialEq for TraceContext { fn eq(&self, other: &Self) -> bool { self.agents == other.agents @@ -353,7 +363,7 @@ impl Trace { if ctx.deterministic_put { if let Ok(agent) = ctx.find_agent_mut(name) { - if let Err(err) = agent.put_mut().set_deterministic() { + if let Err(err) = agent.put_mut().determinism_reseed() { warn!("Unable to make agent {} deterministic: {}", name, err) } } @@ -367,6 +377,9 @@ impl Trace { where PB: ProtocolBehavior, { + // We reseed all PUTs before executing a trace! + ctx.put_registry.determinism_reseed_all_factories(); + for trace in &self.prior_traces { trace.spawn_agents(ctx)?; trace.execute(ctx)?; diff --git a/sshpuffin/src/libssh/mod.rs b/sshpuffin/src/libssh/mod.rs index 32e7b98ca..b36437aa9 100644 --- a/sshpuffin/src/libssh/mod.rs +++ b/sshpuffin/src/libssh/mod.rs @@ -334,7 +334,7 @@ impl Put for LibSSL { fn shutdown(&mut self) -> String { panic!("Not supported") } - fn set_deterministic(&mut self) -> Result<(), puffin::error::Error> { + fn determinism_reseed(&mut self) -> Result<(), puffin::error::Error> { Err(Error::Put( "libssh does not support determinism".to_string(), )) diff --git a/tlspuffin/src/integration_tests/determinism.rs b/tlspuffin/src/integration_tests/determinism.rs new file mode 100644 index 000000000..83b30852b --- /dev/null +++ b/tlspuffin/src/integration_tests/determinism.rs @@ -0,0 +1,50 @@ +use crate::put_registry::TLS_PUT_REGISTRY; +use crate::tls::seeds::seed_client_attacker_full; +use crate::tls::trace_helper::TraceHelper; +use puffin::put::PutOptions; +use puffin::trace::TraceContext; + +#[test] +#[cfg(all(feature = "deterministic", feature = "tls13"))] +fn test_attacker_full_det_recreate() { + // Fail without global rand reset and reseed, BEFORE tracecontext are created (at least for OpenSSL)! + TLS_PUT_REGISTRY.determinism_set_reseed_all_factories(); + + let trace = seed_client_attacker_full.build_trace(); + + let mut ctx_1 = TraceContext::new(&TLS_PUT_REGISTRY, PutOptions::default()); + trace.execute(&mut ctx_1); + + let mut ctx_2 = TraceContext::new(&TLS_PUT_REGISTRY, PutOptions::default()); + trace.execute(&mut ctx_2); + + assert_eq!(ctx_1, ctx_2); + + // For debugging, knowledge by knowledge: + // let server = AgentName::mew(); + // for i in 0..7 { + // let app_data = term!((server, i)[None] > TypeShape::of::()); + // let e_1 = app_data.evaluate(&ctx_1).unwrap(); + // println!("[{i}] Enc{i} OpaqueMessage: {:?}", e_1); + // if let Some(msg_1) = e_1 + // .as_ref() + // .downcast_ref::<::OpaqueProtocolMessage>( + // ) { + // let data_1 = msg_1.clone().encode(); + // println!( + // " Enc{i} OpaqueMessage data length/data in ctx3: {} / {:?}", + // data_1.len(), + // data_1 + // ); + // let e_2 = app_data.evaluate(&ctx_2).unwrap(); + // if let Some(msg_2) = e_2.as_ref().downcast_ref::<::OpaqueProtocolMessage>() { + // let data_2 = msg_2.clone().encode(); + // assert_eq!(data_1, data_2); + // } else { + // panic!("Failed to encode enc{i} OpaqueMessage in ctx_2"); + // } + // } else { + // panic!("Failed to encode enc{i} OpaqueMessage in ctx_1"); + // } + // } +} diff --git a/tlspuffin/src/integration_tests/mod.rs b/tlspuffin/src/integration_tests/mod.rs index 9ca290206..0d4b7447d 100644 --- a/tlspuffin/src/integration_tests/mod.rs +++ b/tlspuffin/src/integration_tests/mod.rs @@ -2,6 +2,8 @@ use crate::put_registry::TLS_PUT_REGISTRY; mod mutations; mod term_zoo; +#[cfg(feature = "deterministic")] +mod determinism; #[test] fn version_test() { diff --git a/tlspuffin/src/openssl/deterministic.rs b/tlspuffin/src/openssl/deterministic.rs index f798af19a..e4af78a02 100644 --- a/tlspuffin/src/openssl/deterministic.rs +++ b/tlspuffin/src/openssl/deterministic.rs @@ -9,10 +9,18 @@ extern "C" { } #[cfg(feature = "deterministic")] -pub fn set_openssl_deterministic() { - warn!("OpenSSL is no longer random!"); +pub fn determinism_set_reseed_openssl() { + println!("Making OpenSSL fully deterministic: reset rand and reseed to a constant..."); unsafe { make_openssl_deterministic(); + } + determinism_reseed_openssl(); +} + +#[cfg(feature = "deterministic")] +pub fn determinism_reseed_openssl() { + println!(" - Reseed RAND for OpenSSL"); + unsafe { let mut seed: [u8; 4] = 42u32.to_le().to_ne_bytes(); let buf = seed.as_mut_ptr(); RAND_seed(buf, 4); @@ -21,38 +29,15 @@ pub fn set_openssl_deterministic() { #[cfg(test)] mod tests { - use crate::put_registry::TLS_PUT_REGISTRY; - use crate::tls::seeds::{create_corpus, seed_client_attacker_full}; - use crate::tls::trace_helper::TraceHelper; + use crate::openssl::deterministic::determinism_set_reseed_openssl; use openssl::rand::rand_bytes; - use puffin::put::PutOptions; - use puffin::trace::{Action, InputAction, OutputAction, Step, Trace, TraceContext}; - use std::fmt::format; + #[test] #[cfg(feature = "openssl111-binding")] fn test_openssl_no_randomness_simple() { - use crate::openssl::deterministic::set_openssl_deterministic; - set_openssl_deterministic(); + determinism_set_reseed_openssl(); let mut buf1 = [0; 2]; rand_bytes(&mut buf1).unwrap(); - assert_eq!(buf1, [70, 100]); - } - - #[test] - fn test_openssl_no_randomness_full() { - use crate::openssl::deterministic::set_openssl_deterministic; - set_openssl_deterministic(); - - let trace = seed_client_attacker_full.build_trace(); - let mut ctx1 = TraceContext::new(&TLS_PUT_REGISTRY, PutOptions::default()); - ctx1.set_deterministic(true); - trace.execute(&mut ctx1); - let mut ctx2 = TraceContext::new(&TLS_PUT_REGISTRY, PutOptions::default()); - ctx2.set_deterministic(true); - trace.execute(&mut ctx2); - - println!("Left: {:#?}\n", ctx1); - println!("Right: {:#?}\n", ctx2); - assert_eq!(ctx1, ctx2); + assert_eq!(buf1, [179, 16]); } } diff --git a/tlspuffin/src/openssl/mod.rs b/tlspuffin/src/openssl/mod.rs index e0337c93c..d2a434b8a 100644 --- a/tlspuffin/src/openssl/mod.rs +++ b/tlspuffin/src/openssl/mod.rs @@ -7,7 +7,7 @@ use std::{ rc::Rc, }; -use log::{info, warn}; +use log::{debug, info, warn}; use openssl::{ error::ErrorStack, pkey::{PKeyRef, Private}, @@ -45,6 +45,7 @@ use crate::{ message::{Message, OpaqueMessage}, }, }; +use crate::openssl::deterministic::{determinism_set_reseed_openssl, determinism_reseed_openssl}; mod bindings; #[cfg(feature = "deterministic")] @@ -102,6 +103,16 @@ pub fn new_openssl_factory() -> Box> { fn version(&self) -> String { OpenSSL::version() } + + fn determinism_set_reseed(&self) -> () { + debug!("[Determinism] set and reseed"); + determinism_set_reseed_openssl(); + } + + fn determinism_reseed(&self) -> () { + debug!("[Determinism] reseed"); + determinism_reseed_openssl(); + } } Box::new(OpenSSLFactory) @@ -316,10 +327,10 @@ impl Put for OpenSSL { .contains("SSL negotiation finished successfully") } - fn set_deterministic(&mut self) -> Result<(), Error> { + fn determinism_reseed(&mut self) -> Result<(), Error> { #[cfg(feature = "deterministic")] { - deterministic::set_openssl_deterministic(); + determinism_reseed_openssl(); Ok(()) } #[cfg(not(feature = "deterministic"))] diff --git a/tlspuffin/src/tcp/mod.rs b/tlspuffin/src/tcp/mod.rs index 5f737c253..0827e7831 100644 --- a/tlspuffin/src/tcp/mod.rs +++ b/tlspuffin/src/tcp/mod.rs @@ -11,7 +11,7 @@ use std::{ time::Duration, }; -use log::error; +use log::{debug, error}; use puffin::{ agent::{AgentDescriptor, AgentName, AgentType}, error::Error, @@ -75,6 +75,14 @@ pub fn new_tcp_factory() -> Box> { fn version(&self) -> String { TcpClientPut::version() } + + fn determinism_set_reseed(&self) -> () { + debug!(" [Determinism] Factory {} has no support for determinism. We cannot set and reseed.", self.name()); + } + + fn determinism_reseed(&self) -> () { + debug!(" [Determinism] Factory {} has no support for determinism. We cannot reseed.", self.name()); + } } Box::new(TCPFactory) @@ -356,9 +364,9 @@ impl Put for TcpServerPut { false } - fn set_deterministic(&mut self) -> Result<(), puffin::error::Error> { + fn determinism_reseed(&mut self) -> Result<(), puffin::error::Error> { Err(Error::Agent( - "Unable to make TCP PUT deterministic!".to_string(), + "[deterministic] Unable to reseed TCP PUT!".to_string(), )) } @@ -411,9 +419,9 @@ impl Put for TcpClientPut { false } - fn set_deterministic(&mut self) -> Result<(), puffin::error::Error> { + fn determinism_reseed(&mut self) -> Result<(), puffin::error::Error> { Err(Error::Agent( - "Unable to make TCP PUT deterministic!".to_string(), + "[deterministic] Unable to reseed TCP PUT!".to_string(), )) } diff --git a/tlspuffin/src/wolfssl/mod.rs b/tlspuffin/src/wolfssl/mod.rs index ce861a9a5..61379f0c8 100644 --- a/tlspuffin/src/wolfssl/mod.rs +++ b/tlspuffin/src/wolfssl/mod.rs @@ -274,9 +274,9 @@ impl Put for WolfSSL { unsafe { version().to_string() } } - fn set_deterministic(&mut self) -> Result<(), puffin::error::Error> { + fn determinism_reseed(&mut self) -> Result<(), puffin::error::Error> { Err(Error::Agent( - "WolfSSL does not support determinism".to_string(), + "[determinism] WolfSSL does not support reseed".to_string(), )) } From 8f1ce5930c0d67491faed328fda59f7b9004756e Mon Sep 17 00:00:00 2001 From: HIRSCHI Lucca Date: Wed, 20 Dec 2023 16:47:35 +0100 Subject: [PATCH 14/63] rustfmt pass --- puffin/src/claims.rs | 2 +- puffin/src/put_registry.rs | 3 +-- puffin/src/trace.rs | 8 ++++++-- tlspuffin/src/integration_tests/mod.rs | 4 ++-- tlspuffin/src/openssl/mod.rs | 2 +- tlspuffin/src/tcp/mod.rs | 5 ++++- 6 files changed, 15 insertions(+), 9 deletions(-) diff --git a/puffin/src/claims.rs b/puffin/src/claims.rs index c1076cf70..360c12203 100644 --- a/puffin/src/claims.rs +++ b/puffin/src/claims.rs @@ -12,7 +12,7 @@ use log::{debug, trace}; use crate::{agent::AgentName, algebra::dynamic_function::TypeShape, variable_data::VariableData}; -pub trait Claim: VariableData + Debug { +pub trait Claim: VariableData + Debug { fn agent_name(&self) -> AgentName; fn id(&self) -> TypeShape; fn inner(&self) -> Box; diff --git a/puffin/src/put_registry.rs b/puffin/src/put_registry.rs index 273152951..6a62efd4a 100644 --- a/puffin/src/put_registry.rs +++ b/puffin/src/put_registry.rs @@ -5,8 +5,8 @@ use crate::{ put::{Put, PutName}, trace::TraceContext, }; -use std::fmt::{Debug, Formatter}; use log::debug; +use std::fmt::{Debug, Formatter}; pub const DUMMY_PUT: PutName = PutName(['D', 'U', 'M', 'Y', 'Y', 'D', 'U', 'M', 'M', 'Y']); @@ -79,5 +79,4 @@ pub trait Factory { fn determinism_set_reseed(&self) -> (); fn determinism_reseed(&self) -> (); - } diff --git a/puffin/src/trace.rs b/puffin/src/trace.rs index 56c262a68..be5f4eccc 100644 --- a/puffin/src/trace.rs +++ b/puffin/src/trace.rs @@ -17,7 +17,7 @@ use core::fmt; use std::{ any::{Any, TypeId}, collections::HashMap, - fmt::{Display,Debug}, + fmt::{Debug, Display}, hash::Hash, marker::PhantomData, }; @@ -114,7 +114,11 @@ pub struct TraceContext { impl fmt::Display for TraceContext { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "Knowledge [not displaying other fields] (size={}):", self.knowledge.len()); + write!( + f, + "Knowledge [not displaying other fields] (size={}):", + self.knowledge.len() + ); for k in &self.knowledge { write!(f, "\n {}, -- {:?}", k, k); } diff --git a/tlspuffin/src/integration_tests/mod.rs b/tlspuffin/src/integration_tests/mod.rs index 0d4b7447d..f57e6b810 100644 --- a/tlspuffin/src/integration_tests/mod.rs +++ b/tlspuffin/src/integration_tests/mod.rs @@ -1,9 +1,9 @@ use crate::put_registry::TLS_PUT_REGISTRY; -mod mutations; -mod term_zoo; #[cfg(feature = "deterministic")] mod determinism; +mod mutations; +mod term_zoo; #[test] fn version_test() { diff --git a/tlspuffin/src/openssl/mod.rs b/tlspuffin/src/openssl/mod.rs index d2a434b8a..7f5547a25 100644 --- a/tlspuffin/src/openssl/mod.rs +++ b/tlspuffin/src/openssl/mod.rs @@ -29,6 +29,7 @@ use puffin::{ }; use smallvec::SmallVec; +use crate::openssl::deterministic::{determinism_reseed_openssl, determinism_set_reseed_openssl}; use crate::{ claims::{ ClaimData, ClaimDataMessage, ClaimDataTranscript, ClientHello, Finished, TlsClaim, @@ -45,7 +46,6 @@ use crate::{ message::{Message, OpaqueMessage}, }, }; -use crate::openssl::deterministic::{determinism_set_reseed_openssl, determinism_reseed_openssl}; mod bindings; #[cfg(feature = "deterministic")] diff --git a/tlspuffin/src/tcp/mod.rs b/tlspuffin/src/tcp/mod.rs index 0827e7831..e91059281 100644 --- a/tlspuffin/src/tcp/mod.rs +++ b/tlspuffin/src/tcp/mod.rs @@ -81,7 +81,10 @@ pub fn new_tcp_factory() -> Box> { } fn determinism_reseed(&self) -> () { - debug!(" [Determinism] Factory {} has no support for determinism. We cannot reseed.", self.name()); + debug!( + " [Determinism] Factory {} has no support for determinism. We cannot reseed.", + self.name() + ); } } From bbbce3d7561d18281f536c0184c8eec62f415828 Mon Sep 17 00:00:00 2001 From: HIRSCHI Lucca Date: Wed, 20 Dec 2023 17:05:28 +0100 Subject: [PATCH 15/63] missing dummy implems --- puffin/src/algebra/mod.rs | 8 ++++++++ sshpuffin/src/libssh/mod.rs | 11 +++++++++++ 2 files changed, 19 insertions(+) diff --git a/puffin/src/algebra/mod.rs b/puffin/src/algebra/mod.rs index fe4f09028..48181c379 100644 --- a/puffin/src/algebra/mod.rs +++ b/puffin/src/algebra/mod.rs @@ -539,6 +539,14 @@ pub mod test_signature { fn version(&self) -> String { panic!("Not implemented for test stub"); } + + fn determinism_set_reseed(&self) -> () { + panic!("Not implemented for test stub"); + } + + fn determinism_reseed(&self) -> () { + panic!("Not implemented for test stub"); + } } } diff --git a/sshpuffin/src/libssh/mod.rs b/sshpuffin/src/libssh/mod.rs index b36437aa9..9ee6f2630 100644 --- a/sshpuffin/src/libssh/mod.rs +++ b/sshpuffin/src/libssh/mod.rs @@ -151,6 +151,17 @@ pub fn new_libssh_factory() -> Box> { fn version(&self) -> String { LibSSL::version() } + + fn determinism_set_reseed(&self) -> () { + debug!(" [Determinism] Factory {} has no support for determinism. We cannot set and reseed.", self.name()); + } + + fn determinism_reseed(&self) -> () { + debug!( + " [Determinism] Factory {} has no support for determinism. We cannot reseed.", + self.name() + ); + } } Box::new(LibSSLFactory) From b1cdbbbbbde555b7c4a41c275bcd380db8cbd16c Mon Sep 17 00:00:00 2001 From: HIRSCHI Lucca Date: Wed, 20 Dec 2023 18:09:52 +0100 Subject: [PATCH 16/63] [det test] testing for many traces -->fails --- tlspuffin/src/integration_tests/determinism.rs | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/tlspuffin/src/integration_tests/determinism.rs b/tlspuffin/src/integration_tests/determinism.rs index 83b30852b..2ad7a577d 100644 --- a/tlspuffin/src/integration_tests/determinism.rs +++ b/tlspuffin/src/integration_tests/determinism.rs @@ -15,10 +15,12 @@ fn test_attacker_full_det_recreate() { let mut ctx_1 = TraceContext::new(&TLS_PUT_REGISTRY, PutOptions::default()); trace.execute(&mut ctx_1); - let mut ctx_2 = TraceContext::new(&TLS_PUT_REGISTRY, PutOptions::default()); - trace.execute(&mut ctx_2); - - assert_eq!(ctx_1, ctx_2); + for i in 0..200 { + println!("Attempt #{i}..."); + let mut ctx_2 = TraceContext::new(&TLS_PUT_REGISTRY, PutOptions::default()); + trace.execute(&mut ctx_2); + assert_eq!(ctx_1, ctx_2); + } // For debugging, knowledge by knowledge: // let server = AgentName::mew(); From 8c2e05e0c5d53f0643345ab81798b9e5b4e3be81 Mon Sep 17 00:00:00 2001 From: HIRSCHI Lucca Date: Wed, 20 Dec 2023 18:29:44 +0100 Subject: [PATCH 17/63] trying to better reseed, still failing test --- crates/openssl-src-111/src/deterministic_rand.c | 14 +++++++------- tlspuffin/src/openssl/deterministic.rs | 15 ++++++++++++++- 2 files changed, 21 insertions(+), 8 deletions(-) diff --git a/crates/openssl-src-111/src/deterministic_rand.c b/crates/openssl-src-111/src/deterministic_rand.c index 842fde7ab..4dc65cc52 100644 --- a/crates/openssl-src-111/src/deterministic_rand.c +++ b/crates/openssl-src-111/src/deterministic_rand.c @@ -2,10 +2,10 @@ #include #include -unsigned int seed = 0; -unsigned int m = 0xFFFFFFFF; -unsigned int a = 22695477; -unsigned int c = 1; +unsigned int tlspuffin_seed = 42; +const unsigned int m = 0xFFFFFFFF; +const unsigned int a = 22695477; +const unsigned int c = 1; #define UNUSED(x) (void)(x) @@ -17,7 +17,7 @@ static int stdlib_rand_seed(const void *buf, int num) { return 0; } - seed = *((unsigned int *) buf); + tlspuffin_seed = *((unsigned int *) buf); return 1; } @@ -27,8 +27,8 @@ static int stdlib_rand_bytes(unsigned char *buf, int num) { for (int index = 0; index < num; ++index) { - seed = (a * seed + c) % m; - buf[index] = seed % 256; + tlspuffin_seed = (a * tlspuffin_seed + c) % m; + buf[index] = tlspuffin_seed % 256; } return 1; } diff --git a/tlspuffin/src/openssl/deterministic.rs b/tlspuffin/src/openssl/deterministic.rs index e4af78a02..606bcadb6 100644 --- a/tlspuffin/src/openssl/deterministic.rs +++ b/tlspuffin/src/openssl/deterministic.rs @@ -6,8 +6,14 @@ use log::warn; extern "C" { fn make_openssl_deterministic(); fn RAND_seed(buf: *mut u8, num: c_int); + pub static mut tlspuffin_seed: u32; } +pub fn get_seed() -> u32 { + unsafe { + return tlspuffin_seed + } +} #[cfg(feature = "deterministic")] pub fn determinism_set_reseed_openssl() { println!("Making OpenSSL fully deterministic: reset rand and reseed to a constant..."); @@ -24,20 +30,27 @@ pub fn determinism_reseed_openssl() { let mut seed: [u8; 4] = 42u32.to_le().to_ne_bytes(); let buf = seed.as_mut_ptr(); RAND_seed(buf, 4); + tlspuffin_seed = 42 as u32; } } #[cfg(test)] mod tests { - use crate::openssl::deterministic::determinism_set_reseed_openssl; + use crate::openssl::deterministic::{determinism_set_reseed_openssl,get_seed}; use openssl::rand::rand_bytes; #[test] #[cfg(feature = "openssl111-binding")] fn test_openssl_no_randomness_simple() { + assert_eq!(get_seed(), 42); determinism_set_reseed_openssl(); + assert_eq!(get_seed(), 42); let mut buf1 = [0; 2]; rand_bytes(&mut buf1).unwrap(); assert_eq!(buf1, [179, 16]); + assert_ne!(get_seed(), 42); + determinism_set_reseed_openssl(); + assert_eq!(get_seed(), 42); + } } From 5f6e43c7f55c6499e78780600cf18083d6a3b516 Mon Sep 17 00:00:00 2001 From: HIRSCHI Lucca Date: Thu, 21 Dec 2023 15:17:09 +0100 Subject: [PATCH 18/63] add attempt at patching OpenSSL312 to have deterministic timestamps --- .../patches/det_timestamp.patch | 54 +++++++++++++++++++ crates/openssl-src-111/src/lib.rs | 33 +++++++++--- tlspuffin/src/openssl/deterministic.rs | 7 +-- 3 files changed, 82 insertions(+), 12 deletions(-) create mode 100644 crates/openssl-src-111/patches/det_timestamp.patch diff --git a/crates/openssl-src-111/patches/det_timestamp.patch b/crates/openssl-src-111/patches/det_timestamp.patch new file mode 100644 index 000000000..8009fbc41 --- /dev/null +++ b/crates/openssl-src-111/patches/det_timestamp.patch @@ -0,0 +1,54 @@ +Subject: [PATCH] deterministic timestamp +--- +Index: crypto/time.c +IDEA additional info: +Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP +<+>UTF-8 +=================================================================== +diff --git a/crypto/time.c b/crypto/time.c +--- a/crypto/time.c (revision f925bfebbb287321133b9251e72bee869a0f58b4) ++++ b/crypto/time.c (date 1703166565605) +@@ -11,6 +11,9 @@ + #include + #include "internal/time.h" + ++unsigned int tlspuffin_time = 42; ++ ++ + OSSL_TIME ossl_time_now(void) + { + OSSL_TIME r; +@@ -32,17 +35,21 @@ + # endif + r.t = ((uint64_t)now.ul) * (OSSL_TIME_SECOND / 10000000); + #else /* defined(_WIN32) */ +- struct timeval t; +- +- if (gettimeofday(&t, NULL) < 0) { +- ERR_raise_data(ERR_LIB_SYS, get_last_sys_error(), +- "calling gettimeofday()"); +- return ossl_time_zero(); +- } +- if (t.tv_sec <= 0) +- r.t = t.tv_usec <= 0 ? 0 : t.tv_usec * OSSL_TIME_US; +- else +- r.t = ((uint64_t)t.tv_sec * 1000000 + t.tv_usec) * OSSL_TIME_US; ++// struct timeval t; ++// ++// if (gettimeofday(&t, NULL) < 0) { ++// ERR_raise_data(ERR_LIB_SYS, get_last_sys_error(), ++// "calling gettimeofday()"); ++// return ossl_time_zero(); ++// } ++// if (t.tv_sec <= 0) ++// r.t = t.tv_usec <= 0 ? 0 : t.tv_usec * OSSL_TIME_US; ++// else ++// r.t = ((uint64_t)t.tv_sec * 1000000 + t.tv_usec) * OSSL_TIME_US; + #endif /* defined(_WIN32) */ +- return r; ++ r = ossl_time_zero(); ++ r.t = r.t + tlspuffin_time * (1000 * OSSL_TIME_US); // 1 seconds elapsed between every call ++ tlspuffin_time++; ++ return ossl_time_zero(); ++// return r; + } diff --git a/crates/openssl-src-111/src/lib.rs b/crates/openssl-src-111/src/lib.rs index 6f2f01115..b790351a6 100644 --- a/crates/openssl-src-111/src/lib.rs +++ b/crates/openssl-src-111/src/lib.rs @@ -1,13 +1,8 @@ extern crate bindgen; extern crate cc; -use std::{ - env, fs, - fs::{canonicalize, File}, - io::Write, - path::{Path, PathBuf}, - process::Command, -}; +use std::{env, fs, fs::{canonicalize, File}, io, io::Write, path::{Path, PathBuf}, process::Command}; +use std::io::ErrorKind; const REF: &str = if cfg!(feature = "openssl101f") { "OpenSSL_1_0_1f" @@ -51,6 +46,24 @@ pub fn version() -> &'static str { env!("CARGO_PKG_VERSION") } +fn patch_openssl>( + out_dir: P, + patch: &str, +) -> std::io::Result<()> { + let root = Path::new(env!("CARGO_MANIFEST_DIR")); + let status = Command::new("git") + .current_dir(out_dir) + .arg("am") + .arg(root.join("patches").join(patch).to_str().unwrap()) + .status()?; + + if !status.success() { + return Err(io::Error::from(ErrorKind::Other)); + } + + Ok(()) +} + pub struct Build { out_dir: Option, target: Option, @@ -140,6 +153,12 @@ impl Build { clone_repo(&inner_dir.to_str().unwrap()).unwrap(); + if cfg!(feature = "openssl312") { + let source_dir = PathBuf::from(env::var("CARGO_MANIFEST_DIR").unwrap()); + // Patching the repo to get deterministic timestamp + patch_openssl(out_dir, "det_timestamp.patch").unwrap(); + } + let perl_program = env::var("OPENSSL_SRC_PERL").unwrap_or(env::var("PERL").unwrap_or("perl".to_string())); let mut configure = Command::new(perl_program); diff --git a/tlspuffin/src/openssl/deterministic.rs b/tlspuffin/src/openssl/deterministic.rs index 606bcadb6..08cfcfcaa 100644 --- a/tlspuffin/src/openssl/deterministic.rs +++ b/tlspuffin/src/openssl/deterministic.rs @@ -10,9 +10,7 @@ extern "C" { } pub fn get_seed() -> u32 { - unsafe { - return tlspuffin_seed - } + unsafe { return tlspuffin_seed } } #[cfg(feature = "deterministic")] pub fn determinism_set_reseed_openssl() { @@ -36,7 +34,7 @@ pub fn determinism_reseed_openssl() { #[cfg(test)] mod tests { - use crate::openssl::deterministic::{determinism_set_reseed_openssl,get_seed}; + use crate::openssl::deterministic::{determinism_set_reseed_openssl, get_seed}; use openssl::rand::rand_bytes; #[test] @@ -51,6 +49,5 @@ mod tests { assert_ne!(get_seed(), 42); determinism_set_reseed_openssl(); assert_eq!(get_seed(), 42); - } } From c2b1758f841c2908a7f71aad5c0044a3d695933c Mon Sep 17 00:00:00 2001 From: Tom Gouville Date: Wed, 10 Jan 2024 15:31:43 +0100 Subject: [PATCH 19/63] improving boringssl build and add randomness test (no working now) --- Cargo.toml | 1 + .../patches/fix-CVE-2022-25638.patch | 190 ------- .../patches/fix-CVE-2022-25640.patch | 126 ----- .../patches/fix-CVE-2022-39173.patch | 62 --- .../patches/fix-CVE-2022-42905.patch | 462 ------------------ crates/boringssl-src/patches/no_asan.patch | 25 + crates/boringssl-src/src/lib.rs | 33 +- crates/boringssl-sys/build.rs | 5 + tlspuffin/Cargo.toml | 6 +- tlspuffin/src/boringssl/bindings.rs | 94 ---- tlspuffin/src/boringssl/deterministic.rs | 47 +- tlspuffin/src/boringssl/mod.rs | 20 +- 12 files changed, 88 insertions(+), 983 deletions(-) delete mode 100644 crates/boringssl-src/patches/fix-CVE-2022-25638.patch delete mode 100644 crates/boringssl-src/patches/fix-CVE-2022-25640.patch delete mode 100644 crates/boringssl-src/patches/fix-CVE-2022-39173.patch delete mode 100644 crates/boringssl-src/patches/fix-CVE-2022-42905.patch create mode 100644 crates/boringssl-src/patches/no_asan.patch delete mode 100644 tlspuffin/src/boringssl/bindings.rs diff --git a/Cargo.toml b/Cargo.toml index c1d784525..cdf429763 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -35,3 +35,4 @@ debug = true [profile.dev] panic = "abort" +lto = true diff --git a/crates/boringssl-src/patches/fix-CVE-2022-25638.patch b/crates/boringssl-src/patches/fix-CVE-2022-25638.patch deleted file mode 100644 index 74040cadf..000000000 --- a/crates/boringssl-src/patches/fix-CVE-2022-25638.patch +++ /dev/null @@ -1,190 +0,0 @@ -From 5e8ee3093c09269f58ba4c77e8cbf59d24e3dec6 Mon Sep 17 00:00:00 2001 -From: David Garske -Date: Tue, 1 Feb 2022 11:28:25 -0800 -Subject: [PATCH 1/2] Fix for mutual authentication to prevent mismatch of - certificate and sig algo. Work from Sean P. ZD 13571 - ---- - src/tls13.c | 53 ++++++++++++++++++++++++++--------------------------- - 1 file changed, 26 insertions(+), 27 deletions(-) - -diff --git a/src/tls13.c b/src/tls13.c -index 7bd68905c..b41ec6980 100644 ---- a/src/tls13.c -+++ b/src/tls13.c -@@ -6536,6 +6536,8 @@ static int DoTls13CertificateVerify(WOLFSSL* ssl, byte* input, - - case TLS_ASYNC_BUILD: - { -+ int validSigAlgo = 0; -+ - /* Signature algorithm. */ - if ((args->idx - args->begin) + ENUM_LEN + ENUM_LEN > totalSz) { - ERROR_OUT(BUFFER_ERROR, exit_dcv); -@@ -6561,53 +6563,50 @@ static int DoTls13CertificateVerify(WOLFSSL* ssl, byte* input, - - /* Check for public key of required type. */ - #ifdef HAVE_ED25519 -- if (args->sigAlgo == ed25519_sa_algo && -- !ssl->peerEd25519KeyPresent) { -- WOLFSSL_MSG("Peer sent ED25519 sig but not ED25519 cert"); -- ret = SIG_VERIFY_E; -- goto exit_dcv; -+ if (args->sigAlgo == ed25519_sa_algo) { -+ WOLFSSL_MSG("Peer sent ED25519 sig"); -+ validSigAlgo = ssl->peerEd25519KeyPresent; - } - #endif - #ifdef HAVE_ED448 -- if (args->sigAlgo == ed448_sa_algo && !ssl->peerEd448KeyPresent) { -- WOLFSSL_MSG("Peer sent ED448 sig but not ED448 cert"); -- ret = SIG_VERIFY_E; -- goto exit_dcv; -+ if (args->sigAlgo == ed448_sa_algo) { -+ WOLFSSL_MSG("Peer sent ED448 sig"); -+ validSigAlgo = ssl->peerEd448KeyPresent; - } - #endif - #ifdef HAVE_ECC -- if (args->sigAlgo == ecc_dsa_sa_algo && -- !ssl->peerEccDsaKeyPresent) { -- WOLFSSL_MSG("Peer sent ECC sig but not ECC cert"); -- ret = SIG_VERIFY_E; -- goto exit_dcv; -+ if (args->sigAlgo == ecc_dsa_sa_algo) { -+ WOLFSSL_MSG("Peer sent ECC sig"); -+ validSigAlgo = ssl->peerEccDsaKeyPresent; - } - #endif - #ifdef HAVE_PQC -- if (args->sigAlgo == falcon_level1_sa_algo && !ssl->peerFalconKeyPresent) { -- WOLFSSL_MSG("Peer sent Falcon Level 1 sig but different cert"); -- ret = SIG_VERIFY_E; -- goto exit_dcv; -+ if (args->sigAlgo == falcon_level1_sa_algo) { -+ WOLFSSL_MSG("Peer sent Falcon Level 1 sig"); -+ validSigAlgo = ssl->peerFalconKeyPresent; - } -- if (args->sigAlgo == falcon_level5_sa_algo && !ssl->peerFalconKeyPresent) { -- WOLFSSL_MSG("Peer sent Falcon Level 5 sig but different cert"); -- ret = SIG_VERIFY_E; -- goto exit_dcv; -+ if (args->sigAlgo == falcon_level5_sa_algo) { -+ WOLFSSL_MSG("Peer sent Falcon Level 5 sig"); -+ validSigAlgo = ssl->peerFalconKeyPresent; - } - #endif - - #ifndef NO_RSA - if (args->sigAlgo == rsa_sa_algo) { -- WOLFSSL_MSG("Peer sent PKCS#1.5 algo but not in certificate"); -+ WOLFSSL_MSG("Peer sent PKCS#1.5 algo - not valid TLS 1.3"); - ERROR_OUT(INVALID_PARAMETER, exit_dcv); - } -- if (args->sigAlgo == rsa_pss_sa_algo && -- (ssl->peerRsaKey == NULL || !ssl->peerRsaKeyPresent)) { -- WOLFSSL_MSG("Peer sent RSA sig but not RSA cert"); -+ if (args->sigAlgo == rsa_pss_sa_algo) { -+ WOLFSSL_MSG("Peer sent RSA sig"); -+ validSigAlgo = (ssl->peerRsaKey != NULL) && -+ ssl->peerRsaKeyPresent; -+ } -+ #endif -+ if (!validSigAlgo) { -+ WOLFSSL_MSG("Sig algo doesn't correspond to certficate"); - ret = SIG_VERIFY_E; - goto exit_dcv; - } -- #endif - - sig->buffer = (byte*)XMALLOC(args->sz, ssl->heap, - DYNAMIC_TYPE_SIGNATURE); --- -2.34.1 - - -From 139416c3dcb3131474df9d82d3517faca9d04e1b Mon Sep 17 00:00:00 2001 -From: David Garske -Date: Fri, 4 Feb 2022 10:57:35 -0800 -Subject: [PATCH 2/2] Add checking to make sure key is present in all cases. - Explicitly set `validSigAlgo` to zero with comment to clarify the default - assumption. - ---- - src/tls13.c | 23 ++++++++++++++--------- - 1 file changed, 14 insertions(+), 9 deletions(-) - -diff --git a/src/tls13.c b/src/tls13.c -index b41ec6980..ce18c35ec 100644 ---- a/src/tls13.c -+++ b/src/tls13.c -@@ -6536,7 +6536,7 @@ static int DoTls13CertificateVerify(WOLFSSL* ssl, byte* input, - - case TLS_ASYNC_BUILD: - { -- int validSigAlgo = 0; -+ int validSigAlgo; - - /* Signature algorithm. */ - if ((args->idx - args->begin) + ENUM_LEN + ENUM_LEN > totalSz) { -@@ -6562,35 +6562,41 @@ static int DoTls13CertificateVerify(WOLFSSL* ssl, byte* input, - } - - /* Check for public key of required type. */ -+ /* Assume invalid unless signature algo matches the key provided */ -+ validSigAlgo = 0; - #ifdef HAVE_ED25519 - if (args->sigAlgo == ed25519_sa_algo) { - WOLFSSL_MSG("Peer sent ED25519 sig"); -- validSigAlgo = ssl->peerEd25519KeyPresent; -+ validSigAlgo = (ssl->peerEd25519Key != NULL) && -+ ssl->peerEd25519KeyPresent; - } - #endif - #ifdef HAVE_ED448 - if (args->sigAlgo == ed448_sa_algo) { - WOLFSSL_MSG("Peer sent ED448 sig"); -- validSigAlgo = ssl->peerEd448KeyPresent; -+ validSigAlgo = (ssl->peerEd448Key != NULL) && -+ ssl->peerEd448KeyPresent; - } - #endif - #ifdef HAVE_ECC - if (args->sigAlgo == ecc_dsa_sa_algo) { - WOLFSSL_MSG("Peer sent ECC sig"); -- validSigAlgo = ssl->peerEccDsaKeyPresent; -+ validSigAlgo = (ssl->peerEccDsaKey != NULL) && -+ ssl->peerEccDsaKeyPresent; - } - #endif - #ifdef HAVE_PQC - if (args->sigAlgo == falcon_level1_sa_algo) { - WOLFSSL_MSG("Peer sent Falcon Level 1 sig"); -- validSigAlgo = ssl->peerFalconKeyPresent; -+ validSigAlgo = (ssl->peerFalconKey != NULL) && -+ ssl->peerFalconKeyPresent; - } - if (args->sigAlgo == falcon_level5_sa_algo) { - WOLFSSL_MSG("Peer sent Falcon Level 5 sig"); -- validSigAlgo = ssl->peerFalconKeyPresent; -+ validSigAlgo = (ssl->peerFalconKey != NULL) && -+ ssl->peerFalconKeyPresent; - } - #endif -- - #ifndef NO_RSA - if (args->sigAlgo == rsa_sa_algo) { - WOLFSSL_MSG("Peer sent PKCS#1.5 algo - not valid TLS 1.3"); -@@ -6604,8 +6610,7 @@ static int DoTls13CertificateVerify(WOLFSSL* ssl, byte* input, - #endif - if (!validSigAlgo) { - WOLFSSL_MSG("Sig algo doesn't correspond to certficate"); -- ret = SIG_VERIFY_E; -- goto exit_dcv; -+ ERROR_OUT(SIG_VERIFY_E, exit_dcv); - } - - sig->buffer = (byte*)XMALLOC(args->sz, ssl->heap, --- -2.34.1 - diff --git a/crates/boringssl-src/patches/fix-CVE-2022-25640.patch b/crates/boringssl-src/patches/fix-CVE-2022-25640.patch deleted file mode 100644 index 7b9739efc..000000000 --- a/crates/boringssl-src/patches/fix-CVE-2022-25640.patch +++ /dev/null @@ -1,126 +0,0 @@ -From f3ff5e38cbd65a029500024853c5a76f6d0c270a Mon Sep 17 00:00:00 2001 -From: David Garske -Date: Fri, 4 Feb 2022 16:36:21 -0800 -Subject: [PATCH 1/2] Improve the client certificate checking logic. Make sure - calling `wolfSSL_CTX_mutual_auth` is also checked. - ---- - src/internal.c | 7 ++++--- - src/tls13.c | 12 ++++++++++++ - wolfssl/internal.h | 4 +++- - 3 files changed, 19 insertions(+), 4 deletions(-) - -diff --git a/src/internal.c b/src/internal.c -index 16b37dc9d..4965662aa 100644 ---- a/src/internal.c -+++ b/src/internal.c -@@ -11136,7 +11136,7 @@ int InitSigPkCb(WOLFSSL* ssl, SignatureCtx* sigCtx) - - - #if !defined(NO_WOLFSSL_CLIENT) || !defined(WOLFSSL_NO_CLIENT_AUTH) --static void DoCertFatalAlert(WOLFSSL* ssl, int ret) -+void DoCertFatalAlert(WOLFSSL* ssl, int ret) - { - int alertWhy; - if (ssl == NULL || ret == 0) { -@@ -31595,7 +31595,8 @@ static int DefTicketEncCb(WOLFSSL* ssl, byte key_name[WOLFSSL_TICKET_NAME_SZ], - } - - #if !defined(NO_CERTS) && !defined(WOLFSSL_NO_CLIENT_AUTH) -- if (ssl->options.verifyPeer && ssl->options.failNoCert) { -+ if (ssl->options.verifyPeer && -+ (ssl->options.mutualAuth || ssl->options.failNoCert)) { - if (!ssl->options.havePeerCert) { - WOLFSSL_MSG("client didn't present peer cert"); - ERROR_OUT(NO_PEER_CERT, exit_dcke); -@@ -31606,7 +31607,7 @@ static int DefTicketEncCb(WOLFSSL* ssl, byte key_name[WOLFSSL_TICKET_NAME_SZ], - if (!ssl->options.havePeerCert && - !ssl->options.usingPSK_cipher) { - WOLFSSL_MSG("client didn't present peer cert"); -- return NO_PEER_CERT; -+ ERROR_OUT(NO_PEER_CERT, exit_dcke); - } - } - #endif /* !NO_CERTS && !WOLFSSL_NO_CLIENT_AUTH */ -diff --git a/src/tls13.c b/src/tls13.c -index 7bd68905c..0b95458c7 100644 ---- a/src/tls13.c -+++ b/src/tls13.c -@@ -6884,6 +6884,18 @@ int DoTls13Finished(WOLFSSL* ssl, const byte* input, word32* inOutIdx, - WOLFSSL_START(WC_FUNC_FINISHED_DO); - WOLFSSL_ENTER("DoTls13Finished"); - -+#if !defined(NO_CERTS) && !defined(WOLFSSL_NO_CLIENT_AUTH) -+ /* verify the client sent certificate if required */ -+ if (ssl->options.side == WOLFSSL_SERVER_END && -+ (ssl->options.mutualAuth || ssl->options.failNoCert)) { -+ if (!ssl->options.havePeerVerify && !ssl->options.resuming) { -+ ret = NO_PEER_CERT; -+ DoCertFatalAlert(ssl, ret); -+ return ret; -+ } -+ } -+#endif -+ - /* check against totalSz */ - if (*inOutIdx + size + ssl->keys.padSz > totalSz) - return BUFFER_E; -diff --git a/wolfssl/internal.h b/wolfssl/internal.h -index ec3ea5502..30c5363d4 100644 ---- a/wolfssl/internal.h -+++ b/wolfssl/internal.h -@@ -4882,7 +4882,9 @@ WOLFSSL_LOCAL int BuildTlsFinished(WOLFSSL* ssl, Hashes* hashes, - WOLFSSL_LOCAL void FreeArrays(WOLFSSL* ssl, int keep); - WOLFSSL_LOCAL int CheckAvailableSize(WOLFSSL *ssl, int size); - WOLFSSL_LOCAL int GrowInputBuffer(WOLFSSL* ssl, int size, int usedLength); -- -+#if !defined(NO_WOLFSSL_CLIENT) || !defined(WOLFSSL_NO_CLIENT_AUTH) -+WOLFSSL_LOCAL void DoCertFatalAlert(WOLFSSL* ssl, int ret); -+#endif - #ifndef NO_TLS - WOLFSSL_LOCAL int MakeTlsMasterSecret(WOLFSSL*); - #ifndef WOLFSSL_AEAD_ONLY --- -2.34.1 - - -From ed49457e2ed0d71537fec588a28047a6994a7a2b Mon Sep 17 00:00:00 2001 -From: David Garske -Date: Mon, 7 Feb 2022 16:26:18 -0800 -Subject: [PATCH 2/2] Include the `havePeerCert` argument in the check. - ---- - src/tls13.c | 9 ++++++--- - 1 file changed, 6 insertions(+), 3 deletions(-) - -diff --git a/src/tls13.c b/src/tls13.c -index 0b95458c7..259ad8a1c 100644 ---- a/src/tls13.c -+++ b/src/tls13.c -@@ -71,6 +71,8 @@ - * You cannot use wc_psk_client_cs_callback type callback on client. - * WOLFSSL_CHECK_ALERT_ON_ERR - * Check for alerts during the handshake in the event of an error. -+ * WOLFSSL_NO_CLIENT_CERT_ERROR -+ * Requires client to set a client certificate - */ - - #ifdef HAVE_CONFIG_H -@@ -6886,10 +6888,11 @@ int DoTls13Finished(WOLFSSL* ssl, const byte* input, word32* inOutIdx, - - #if !defined(NO_CERTS) && !defined(WOLFSSL_NO_CLIENT_AUTH) - /* verify the client sent certificate if required */ -- if (ssl->options.side == WOLFSSL_SERVER_END && -+ if (ssl->options.side == WOLFSSL_SERVER_END && !ssl->options.resuming && - (ssl->options.mutualAuth || ssl->options.failNoCert)) { -- if (!ssl->options.havePeerVerify && !ssl->options.resuming) { -- ret = NO_PEER_CERT; -+ if (!ssl->options.havePeerCert || !ssl->options.havePeerVerify) { -+ ret = NO_PEER_CERT; /* NO_PEER_VERIFY */ -+ WOLFSSL_MSG("TLS v1.3 client did not present peer cert"); - DoCertFatalAlert(ssl, ret); - return ret; - } --- -2.34.1 - diff --git a/crates/boringssl-src/patches/fix-CVE-2022-39173.patch b/crates/boringssl-src/patches/fix-CVE-2022-39173.patch deleted file mode 100644 index 9c1467c78..000000000 --- a/crates/boringssl-src/patches/fix-CVE-2022-39173.patch +++ /dev/null @@ -1,62 +0,0 @@ -From 2aabe592c4f8c64deb9457e4f8e74b7f60ebaf86 Mon Sep 17 00:00:00 2001 -From: Maximilian Ammann -Date: Wed, 19 Oct 2022 13:29:09 +0200 -Subject: [PATCH] Fix CVE-2022-39173 - ---- - src/tls13.c | 34 ++++++++++++++++++++++++++++------ - 1 file changed, 28 insertions(+), 6 deletions(-) - -diff --git a/src/tls13.c b/src/tls13.c -index 7bd68905c..7bab7142f 100644 ---- a/src/tls13.c -+++ b/src/tls13.c -@@ -3834,17 +3834,39 @@ static void RefineSuites(WOLFSSL* ssl, Suites* peerSuites) - { - byte suites[WOLFSSL_MAX_SUITE_SZ]; - word16 suiteSz = 0; -- word16 i, j; -+ word16 i; -+ word16 j; - - XMEMSET(suites, 0, WOLFSSL_MAX_SUITE_SZ); - -- for (i = 0; i < ssl->suites->suiteSz; i += 2) { -+ if (!ssl->options.useClientOrder) { -+ /* Server order refining. */ -+ for (i = 0; i < ssl->suites->suiteSz; i += 2) { -+ for (j = 0; j < peerSuites->suiteSz; j += 2) { -+ if ((ssl->suites->suites[i+0] == peerSuites->suites[j+0]) && -+ (ssl->suites->suites[i+1] == peerSuites->suites[j+1])) { -+ suites[suiteSz++] = peerSuites->suites[j+0]; -+ suites[suiteSz++] = peerSuites->suites[j+1]; -+ break; -+ } -+ } -+ if (suiteSz == WOLFSSL_MAX_SUITE_SZ) -+ break; -+ } -+ } -+ else { -+ /* Client order refining. */ - for (j = 0; j < peerSuites->suiteSz; j += 2) { -- if (ssl->suites->suites[i+0] == peerSuites->suites[j+0] && -- ssl->suites->suites[i+1] == peerSuites->suites[j+1]) { -- suites[suiteSz++] = peerSuites->suites[j+0]; -- suites[suiteSz++] = peerSuites->suites[j+1]; -+ for (i = 0; i < ssl->suites->suiteSz; i += 2) { -+ if ((ssl->suites->suites[i+0] == peerSuites->suites[j+0]) && -+ (ssl->suites->suites[i+1] == peerSuites->suites[j+1])) { -+ suites[suiteSz++] = peerSuites->suites[j+0]; -+ suites[suiteSz++] = peerSuites->suites[j+1]; -+ break; -+ } - } -+ if (suiteSz == WOLFSSL_MAX_SUITE_SZ) -+ break; - } - } - --- -2.34.1 - diff --git a/crates/boringssl-src/patches/fix-CVE-2022-42905.patch b/crates/boringssl-src/patches/fix-CVE-2022-42905.patch deleted file mode 100644 index 377d947bf..000000000 --- a/crates/boringssl-src/patches/fix-CVE-2022-42905.patch +++ /dev/null @@ -1,462 +0,0 @@ -From f19e7daf5b60e253042600424c18d008c526d828 Mon Sep 17 00:00:00 2001 -From: JacobBarthelmeh -Date: Tue, 11 Oct 2022 13:14:59 -0700 -Subject: [PATCH] additional sanity checks on debug callback - ---- - examples/client/client.c | 4 ++ - examples/server/server.c | 7 ++++ - src/internal.c | 84 +++++++++++++++++++++++----------------- - src/tls13.c | 35 +++++++++-------- - wolfssl/internal.h | 3 +- - wolfssl/test.h | 20 ++++++++++ - 6 files changed, 100 insertions(+), 53 deletions(-) - -diff --git a/examples/client/client.c b/examples/client/client.c -index 6e1660472..e7ca8de62 100644 ---- a/examples/client/client.c -+++ b/examples/client/client.c -@@ -1843,6 +1843,7 @@ static int client_srtp_test(WOLFSSL *ssl, func_args *args) - } - #endif /* WOLFSSL_SRTP */ - -+ - THREAD_RETURN WOLFSSL_THREAD client_test(void* args) - { - SOCKET_T sockfd = WOLFSSL_SOCKET_INVALID; -@@ -2930,6 +2931,9 @@ THREAD_RETURN WOLFSSL_THREAD client_test(void* args) - ctx = wolfSSL_CTX_new_ex(method(heap), heap); - if (ctx == NULL) - err_sys("unable to get ctx"); -+#ifdef WOLFSSL_CALLBACKS -+ wolfSSL_CTX_set_msg_callback(ctx, msgDebugCb); -+#endif - - if (wolfSSL_CTX_load_static_memory(&ctx, NULL, memoryIO, sizeof(memoryIO), - WOLFMEM_IO_POOL_FIXED | WOLFMEM_TRACK_STATS, 1) != WOLFSSL_SUCCESS) { -diff --git a/examples/server/server.c b/examples/server/server.c -index f3ff44469..ee3ed7aa6 100644 ---- a/examples/server/server.c -+++ b/examples/server/server.c -@@ -1368,6 +1368,7 @@ static int server_srtp_test(WOLFSSL *ssl, func_args *args) - } - #endif - -+ - THREAD_RETURN WOLFSSL_THREAD server_test(void* args) - { - SOCKET_T sockfd = WOLFSSL_SOCKET_INVALID; -@@ -2336,6 +2337,9 @@ THREAD_RETURN WOLFSSL_THREAD server_test(void* args) - if (method != NULL) { - ctx = SSL_CTX_new(method(NULL)); - } -+#ifdef WOLFSSL_CALLBACKS -+ wolfSSL_CTX_set_msg_callback(ctx, msgDebugCb); -+#endif - #endif /* WOLFSSL_STATIC_MEMORY */ - if (ctx == NULL) - err_sys_ex(catastrophic, "unable to get ctx"); -@@ -3581,6 +3585,9 @@ exit: - #ifdef HAVE_SECURE_RENEGOTIATION - (void) forceScr; - #endif -+#if defined(WOLFSSL_CALLBACKS) && defined(WOLFSSL_EARLY_DATA) -+ (void) earlyData; -+#endif - #ifndef WOLFSSL_TIRTOS - return 0; - #endif -diff --git a/src/internal.c b/src/internal.c -index 930769152..7d5f20f80 100644 ---- a/src/internal.c -+++ b/src/internal.c -@@ -9208,7 +9208,7 @@ static int SendHandshakeMsg(WOLFSSL* ssl, byte* input, word32 inputSz, - } - if (ssl->toInfoOn) { - AddPacketInfo(ssl, packetName, handshake, -- output, outputSz, WRITE_PROTO, ssl->heap); -+ output, outputSz, WRITE_PROTO, 0, ssl->heap); - } - #endif - ssl->fragOffset += fragSz; -@@ -14739,11 +14739,12 @@ static int DoHandShakeMsgType(WOLFSSL* ssl, byte* input, word32* inOutIdx, - } - - #if defined(WOLFSSL_CALLBACKS) || defined(OPENSSL_EXTRA) -- /* add name later, add on record and handshake header part back on */ -+ /* add name later, add the handshake header part back on and record layer -+ * header */ - if (ssl->toInfoOn) { -- int add = RECORD_HEADER_SZ + HANDSHAKE_HEADER_SZ; -- AddPacketInfo(ssl, 0, handshake, input + *inOutIdx - add, -- size + add, READ_PROTO, ssl->heap); -+ AddPacketInfo(ssl, 0, handshake, input + *inOutIdx - -+ HANDSHAKE_HEADER_SZ, size + HANDSHAKE_HEADER_SZ, READ_PROTO, -+ RECORD_HEADER_SZ, ssl->heap); - #ifdef WOLFSSL_CALLBACKS - AddLateRecordHeader(&ssl->curRL, &ssl->timeoutInfo); - #endif -@@ -17792,11 +17793,14 @@ static int DoAlert(WOLFSSL* ssl, byte* input, word32* inOutIdx, int* type) - #if defined(WOLFSSL_CALLBACKS) || defined(OPENSSL_EXTRA) - if (ssl->hsInfoOn) - AddPacketName(ssl, "Alert"); -- if (ssl->toInfoOn) -+ if (ssl->toInfoOn) { - /* add record header back on to info + alert bytes level/code */ -- AddPacketInfo(ssl, "Alert", alert, input + *inOutIdx - -- RECORD_HEADER_SZ, RECORD_HEADER_SZ + ALERT_SIZE, -- READ_PROTO, ssl->heap); -+ AddPacketInfo(ssl, "Alert", alert, input + *inOutIdx, ALERT_SIZE, -+ READ_PROTO, RECORD_HEADER_SZ, ssl->heap); -+ #ifdef WOLFSSL_CALLBACKS -+ AddLateRecordHeader(&ssl->curRL, &ssl->timeoutInfo); -+ #endif -+ } - #endif - - if (IsEncryptionOn(ssl, 0)) { -@@ -18815,9 +18819,8 @@ int ProcessReplyEx(WOLFSSL* ssl, int allowSocketErr) - AddPacketInfo(ssl, "ChangeCipher", - change_cipher_spec, - ssl->buffers.inputBuffer.buffer + -- ssl->buffers.inputBuffer.idx - RECORD_HEADER_SZ - -- (ssl->options.dtls ? DTLS_RECORD_EXTRA : 0), -- 1 + RECORD_HEADER_SZ, READ_PROTO, ssl->heap); -+ ssl->buffers.inputBuffer.idx, -+ 1, READ_PROTO, RECORD_HEADER_SZ, ssl->heap); - #ifdef WOLFSSL_CALLBACKS - AddLateRecordHeader(&ssl->curRL, &ssl->timeoutInfo); - #endif -@@ -19190,7 +19193,7 @@ int SendChangeCipher(WOLFSSL* ssl) - if (ssl->hsInfoOn) AddPacketName(ssl, "ChangeCipher"); - if (ssl->toInfoOn) - AddPacketInfo(ssl, "ChangeCipher", change_cipher_spec, output, -- sendSz, WRITE_PROTO, ssl->heap); -+ sendSz, WRITE_PROTO, 0, ssl->heap); - #endif - ssl->buffers.outputBuffer.length += sendSz; - -@@ -20106,7 +20109,7 @@ int SendFinished(WOLFSSL* ssl) - if (ssl->hsInfoOn) AddPacketName(ssl, "Finished"); - if (ssl->toInfoOn) - AddPacketInfo(ssl, "Finished", handshake, output, sendSz, -- WRITE_PROTO, ssl->heap); -+ WRITE_PROTO, 0, ssl->heap); - #endif - - ssl->buffers.outputBuffer.length += sendSz; -@@ -20548,7 +20551,7 @@ int SendCertificate(WOLFSSL* ssl) - AddPacketName(ssl, "Certificate"); - if (ssl->toInfoOn) - AddPacketInfo(ssl, "Certificate", handshake, output, sendSz, -- WRITE_PROTO, ssl->heap); -+ WRITE_PROTO, 0, ssl->heap); - #endif - - ssl->buffers.outputBuffer.length += sendSz; -@@ -20744,7 +20747,7 @@ int SendCertificateRequest(WOLFSSL* ssl) - AddPacketName(ssl, "CertificateRequest"); - if (ssl->toInfoOn) - AddPacketInfo(ssl, "CertificateRequest", handshake, output, sendSz, -- WRITE_PROTO, ssl->heap); -+ WRITE_PROTO, 0, ssl->heap); - #endif - ssl->buffers.outputBuffer.length += sendSz; - if (ssl->options.groupMessages) -@@ -20858,7 +20861,7 @@ static int BuildCertificateStatus(WOLFSSL* ssl, byte type, buffer* status, - AddPacketName(ssl, "CertificateStatus"); - if (ret == 0 && ssl->toInfoOn) - AddPacketInfo(ssl, "CertificateStatus", handshake, output, sendSz, -- WRITE_PROTO, ssl->heap); -+ WRITE_PROTO, 0, ssl->heap); - #endif - - if (ret == 0) { -@@ -21705,7 +21708,7 @@ static int SendAlert_ex(WOLFSSL* ssl, int severity, int type) - if (ssl->hsInfoOn) - AddPacketName(ssl, "Alert"); - if (ssl->toInfoOn) -- AddPacketInfo(ssl, "Alert", alert, output, sendSz, WRITE_PROTO, -+ AddPacketInfo(ssl, "Alert", alert, output, sendSz, WRITE_PROTO, 0, - ssl->heap); - #endif - -@@ -23785,17 +23788,22 @@ int PickHashSigAlgo(WOLFSSL* ssl, const byte* hashSigAlgo, word32 hashSigAlgoSz) - * type type of packet being sent - * data data bing sent with packet - * sz size of data buffer -+ * lateRL save space for record layer in TimoutInfo struct - * written 1 if this packet is being written to wire, 0 if being read - * heap custom heap to use for mallocs/frees - */ - void AddPacketInfo(WOLFSSL* ssl, const char* name, int type, -- const byte* data, int sz, int written, void* heap) -+ const byte* data, int sz, int written, int lateRL, void* heap) - { - #ifdef WOLFSSL_CALLBACKS - TimeoutInfo* info = &ssl->timeoutInfo; - - if (info->numberPackets < (MAX_PACKETS_HANDSHAKE - 1)) { - WOLFSSL_TIMEVAL currTime; -+ int totalSz; -+ -+ /* add in space for post record layer */ -+ totalSz = sz + lateRL; - - /* may add name after */ - if (name) { -@@ -23805,18 +23813,24 @@ int PickHashSigAlgo(WOLFSSL* ssl, const byte* hashSigAlgo, word32 hashSigAlgoSz) - } - - /* add data, put in buffer if bigger than static buffer */ -- info->packets[info->numberPackets].valueSz = sz; -- if (sz < MAX_VALUE_SZ) -- XMEMCPY(info->packets[info->numberPackets].value, data, sz); -+ info->packets[info->numberPackets].valueSz = totalSz; -+ if (totalSz < MAX_VALUE_SZ) { -+ XMEMCPY(info->packets[info->numberPackets].value, data + lateRL, -+ sz); -+ } - else { - info->packets[info->numberPackets].bufferValue = -- (byte*)XMALLOC(sz, heap, DYNAMIC_TYPE_INFO); -- if (!info->packets[info->numberPackets].bufferValue) -+ (byte*)XMALLOC(totalSz, heap, DYNAMIC_TYPE_INFO); -+ if (!info->packets[info->numberPackets].bufferValue) { - /* let next alloc catch, just don't fill, not fatal here */ - info->packets[info->numberPackets].valueSz = 0; -- else -- XMEMCPY(info->packets[info->numberPackets].bufferValue, -- data, sz); -+ } -+ else { -+ /* copy over data (which has the handshake header), leaving -+ * room for post record layer header if set */ -+ XMEMCPY(info->packets[info->numberPackets].bufferValue + -+ lateRL, data, sz); -+ } - } - gettimeofday(&currTime, 0); - info->packets[info->numberPackets].timestamp.tv_sec = -@@ -23827,7 +23841,7 @@ int PickHashSigAlgo(WOLFSSL* ssl, const byte* hashSigAlgo, word32 hashSigAlgoSz) - } - #endif /* WOLFSSL_CALLBACKS */ - #ifdef OPENSSL_EXTRA -- if (ssl->protoMsgCb != NULL && sz > RECORD_HEADER_SZ) { -+ if ((ssl->protoMsgCb != NULL) && (sz > 0)) { - /* version from hex to dec 16 is 16^1, 256 from 16^2 and - 4096 from 16^3 */ - int version = (ssl->version.minor & 0x0F) + -@@ -23836,8 +23850,7 @@ int PickHashSigAlgo(WOLFSSL* ssl, const byte* hashSigAlgo, word32 hashSigAlgoSz) - ((ssl->version.major & 0xF0) << 12); - - ssl->protoMsgCb(written, version, type, -- (const void *)(data + RECORD_HEADER_SZ), -- (size_t)(sz - RECORD_HEADER_SZ), -+ (const void *)data, (size_t)sz, - ssl, ssl->protoMsgCtx); - } - #endif /* OPENSSL_EXTRA */ -@@ -23846,6 +23859,7 @@ int PickHashSigAlgo(WOLFSSL* ssl, const byte* hashSigAlgo, word32 hashSigAlgoSz) - (void)heap; - (void)type; - (void)ssl; -+ (void)lateRL; - } - - #endif /* WOLFSSL_CALLBACKS */ -@@ -24655,7 +24669,7 @@ static int HashSkeData(WOLFSSL* ssl, enum wc_HashType hashType, - if (ssl->hsInfoOn) AddPacketName(ssl, "ClientHello"); - if (ssl->toInfoOn) - AddPacketInfo(ssl, "ClientHello", handshake, output, sendSz, -- WRITE_PROTO, ssl->heap); -+ WRITE_PROTO, 0, ssl->heap); - #endif - - ssl->options.buildingMsg = 0; -@@ -27964,7 +27978,7 @@ int SendClientKeyExchange(WOLFSSL* ssl) - AddPacketName(ssl, "ClientKeyExchange"); - if (ssl->toInfoOn) - AddPacketInfo(ssl, "ClientKeyExchange", handshake, -- args->output, args->sendSz, WRITE_PROTO, ssl->heap); -+ args->output, args->sendSz, WRITE_PROTO, 0, ssl->heap); - #endif - - ssl->buffers.outputBuffer.length += args->sendSz; -@@ -28973,7 +28987,7 @@ static int DoSessionTicket(WOLFSSL* ssl, const byte* input, word32* inOutIdx, - AddPacketName(ssl, "ServerHello"); - if (ssl->toInfoOn) - AddPacketInfo(ssl, "ServerHello", handshake, output, sendSz, -- WRITE_PROTO, ssl->heap); -+ WRITE_PROTO, 0, ssl->heap); - #endif - - ssl->options.serverState = SERVER_HELLO_COMPLETE; -@@ -32296,7 +32310,7 @@ static int DoSessionTicket(WOLFSSL* ssl, const byte* input, word32* inOutIdx, - AddPacketName(ssl, "ServerHelloDone"); - if (ssl->toInfoOn) - AddPacketInfo(ssl, "ServerHelloDone", handshake, output, sendSz, -- WRITE_PROTO, ssl->heap); -+ WRITE_PROTO, 0, ssl->heap); - #endif - ssl->options.serverState = SERVER_HELLODONE_COMPLETE; - ssl->options.buildingMsg = 0; -@@ -33424,7 +33438,7 @@ static int DefTicketEncCb(WOLFSSL* ssl, byte key_name[WOLFSSL_TICKET_NAME_SZ], - AddPacketName(ssl, "HelloVerifyRequest"); - if (ssl->toInfoOn) - AddPacketInfo(ssl, "HelloVerifyRequest", handshake, output, -- sendSz, WRITE_PROTO, ssl->heap); -+ sendSz, WRITE_PROTO, 0, ssl->heap); - #endif - - /* are we in scr */ -diff --git a/src/tls13.c b/src/tls13.c -index dec6d88c4..655934e4e 100644 ---- a/src/tls13.c -+++ b/src/tls13.c -@@ -3609,7 +3609,7 @@ int SendTls13ClientHello(WOLFSSL* ssl) - if (ssl->hsInfoOn) AddPacketName(ssl, "ClientHello"); - if (ssl->toInfoOn) { - AddPacketInfo(ssl, "ClientHello", handshake, args->output, args->sendSz, -- WRITE_PROTO, ssl->heap); -+ WRITE_PROTO, 0, ssl->heap); - } - #endif - -@@ -5717,7 +5717,7 @@ int SendTls13ServerHello(WOLFSSL* ssl, byte extMsgType) - AddPacketName(ssl, "ServerHello"); - if (ssl->toInfoOn) { - AddPacketInfo(ssl, "ServerHello", handshake, output, sendSz, -- WRITE_PROTO, ssl->heap); -+ WRITE_PROTO, 0, ssl->heap); - } - #endif - -@@ -5852,7 +5852,7 @@ static int SendTls13EncryptedExtensions(WOLFSSL* ssl) - AddPacketName(ssl, "EncryptedExtensions"); - if (ssl->toInfoOn) { - AddPacketInfo(ssl, "EncryptedExtensions", handshake, output, -- sendSz, WRITE_PROTO, ssl->heap); -+ sendSz, WRITE_PROTO, 0, ssl->heap); - } - #endif - -@@ -5988,7 +5988,7 @@ static int SendTls13CertificateRequest(WOLFSSL* ssl, byte* reqCtx, - AddPacketName(ssl, "CertificateRequest"); - if (ssl->toInfoOn) { - AddPacketInfo(ssl, "CertificateRequest", handshake, output, -- sendSz, WRITE_PROTO, ssl->heap); -+ sendSz, WRITE_PROTO, 0, ssl->heap); - } - #endif - -@@ -6734,7 +6734,7 @@ static int SendTls13Certificate(WOLFSSL* ssl) - AddPacketName(ssl, "Certificate"); - if (ssl->toInfoOn) { - AddPacketInfo(ssl, "Certificate", handshake, output, -- sendSz, WRITE_PROTO, ssl->heap); -+ sendSz, WRITE_PROTO, 0, ssl->heap); - } - #endif - -@@ -7214,7 +7214,8 @@ static int SendTls13CertificateVerify(WOLFSSL* ssl) - AddPacketName(ssl, "CertificateVerify"); - if (ssl->toInfoOn) { - AddPacketInfo(ssl, "CertificateVerify", handshake, -- args->output, args->sendSz, WRITE_PROTO, ssl->heap); -+ args->output, args->sendSz, WRITE_PROTO, 0, -+ ssl->heap); - } - #endif - -@@ -8052,7 +8053,7 @@ static int SendTls13Finished(WOLFSSL* ssl) - if (ssl->hsInfoOn) AddPacketName(ssl, "Finished"); - if (ssl->toInfoOn) { - AddPacketInfo(ssl, "Finished", handshake, output, sendSz, -- WRITE_PROTO, ssl->heap); -+ WRITE_PROTO, 0, ssl->heap); - } - #endif - -@@ -8254,13 +8255,13 @@ static int SendTls13KeyUpdate(WOLFSSL* ssl) - if (sendSz < 0) - return BUILD_MSG_ERROR; - -- #ifdef WOLFSSL_CALLBACKS -- if (ssl->hsInfoOn) AddPacketName(ssl, "KeyUpdate"); -- if (ssl->toInfoOn) { -- AddPacketInfo(ssl, "KeyUpdate", handshake, output, sendSz, -- WRITE_PROTO, ssl->heap); -- } -- #endif -+ #if defined(WOLFSSL_CALLBACKS) || defined(OPENSSL_EXTRA) -+ if (ssl->hsInfoOn) AddPacketName(ssl, "KeyUpdate"); -+ if (ssl->toInfoOn) { -+ AddPacketInfo(ssl, "KeyUpdate", handshake, output, sendSz, -+ WRITE_PROTO, 0, ssl->heap); -+ } -+ #endif - - ssl->buffers.outputBuffer.length += sendSz; - -@@ -9327,9 +9328,9 @@ int DoTls13HandShakeMsgType(WOLFSSL* ssl, byte* input, word32* inOutIdx, - #ifdef WOLFSSL_CALLBACKS - /* add name later, add on record and handshake header part back on */ - if (ssl->toInfoOn) { -- int add = RECORD_HEADER_SZ + HANDSHAKE_HEADER_SZ; -- AddPacketInfo(ssl, 0, handshake, input + *inOutIdx - add, -- size + add, READ_PROTO, ssl->heap); -+ AddPacketInfo(ssl, 0, handshake, input + *inOutIdx - -+ HANDSHAKE_HEADER_SZ, size + HANDSHAKE_HEADER_SZ, READ_PROTO, -+ RECORD_HEADER_SZ, ssl->heap); - AddLateRecordHeader(&ssl->curRL, &ssl->timeoutInfo); - } - #endif -diff --git a/wolfssl/internal.h b/wolfssl/internal.h -index d2833f72d..6465dd6f4 100644 ---- a/wolfssl/internal.h -+++ b/wolfssl/internal.h -@@ -4945,7 +4945,8 @@ WOLFSSL_API void SSL_ResourceFree(WOLFSSL* ssl); /* Micrium uses */ - WOLFSSL_LOCAL void InitTimeoutInfo(TimeoutInfo* info); - WOLFSSL_LOCAL void FreeTimeoutInfo(TimeoutInfo* info, void* heap); - WOLFSSL_LOCAL void AddPacketInfo(WOLFSSL* ssl, const char* name, int type, -- const byte* data, int sz, int written, void* heap); -+ const byte* data, int sz, int written, int lateRL, -+ void* heap); - WOLFSSL_LOCAL void AddLateName(const char* name, TimeoutInfo* info); - WOLFSSL_LOCAL void AddLateRecordHeader(const RecordLayerHeader* rl, - TimeoutInfo* info); -diff --git a/wolfssl/test.h b/wolfssl/test.h -index c0abc7304..1e77f6824 100644 ---- a/wolfssl/test.h -+++ b/wolfssl/test.h -@@ -2500,6 +2500,26 @@ static WC_INLINE unsigned int my_psk_client_cs_cb(WOLFSSL* ssl, - #endif - #endif /* USE_WINDOWS_API */ - -+#ifdef WOLFSSL_CALLBACKS -+/* only for debug use! */ -+static WC_INLINE void msgDebugCb(int write_p, int version, int content_type, -+ const void *buf, size_t len, WOLFSSL *ssl, void *arg) -+{ -+ size_t z; -+ byte* pt; -+ -+ printf("Version %02X, content type = %d\n", version, content_type); -+ printf("%s ", (write_p)? "WRITING" : "READING"); -+ pt = (byte*)buf; -+ printf("DATA [%zu]: ", len); -+ for (z = 0; z < len; z++) -+ printf("%02X", pt[z]); -+ printf("\n"); -+ -+ (void)arg; -+ (void)ssl; -+} -+#endif /* WOLFSSL_CALLBACKS */ - - #if defined(HAVE_OCSP) && defined(WOLFSSL_NONBLOCK_OCSP) - static WC_INLINE int OCSPIOCb(void* ioCtx, const char* url, int urlSz, --- -2.34.1 - diff --git a/crates/boringssl-src/patches/no_asan.patch b/crates/boringssl-src/patches/no_asan.patch new file mode 100644 index 000000000..26c42b399 --- /dev/null +++ b/crates/boringssl-src/patches/no_asan.patch @@ -0,0 +1,25 @@ +From c1d43d464d33140729f2524afad1d1bcf63267f7 Mon Sep 17 00:00:00 2001 +From: Tom Gouville +Date: Tue, 9 Jan 2024 17:55:47 +0100 +Subject: [PATCH] oui + +--- + CMakeLists.txt | 2 -- + 1 file changed, 2 deletions(-) + +diff --git a/CMakeLists.txt b/CMakeLists.txt +index 4c7926d00..3f8275a41 100644 +--- a/CMakeLists.txt ++++ b/CMakeLists.txt +@@ -247,8 +247,6 @@ if(FUZZ) + set(RUNNER_ARGS ${RUNNER_ARGS} "-fuzzer" "-shim-config" "fuzzer_mode.json") + endif() + +- set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fsanitize=address,fuzzer-no-link -fsanitize-coverage=edge,indirect-calls") +- set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=address,fuzzer-no-link -fsanitize-coverage=edge,indirect-calls") + endif() + + add_definitions(-DBORINGSSL_IMPLEMENTATION) +-- +2.43.0 + diff --git a/crates/boringssl-src/src/lib.rs b/crates/boringssl-src/src/lib.rs index d5046a832..74185662d 100644 --- a/crates/boringssl-src/src/lib.rs +++ b/crates/boringssl-src/src/lib.rs @@ -1,6 +1,6 @@ use std::{ collections::HashSet, - io, + fs, io, io::ErrorKind, path::{Path, PathBuf}, process::Command, @@ -39,16 +39,14 @@ impl bindgen::callbacks::ParseCallbacks for IgnoreMacros { } } -fn _patch_boringssl>( - source_dir: &PathBuf, - out_dir: P, - patch: &str, -) -> std::io::Result<()> { +fn patch_boringssl>(out_dir: P, patch: &str) -> std::io::Result<()> { + let patch_path = Path::new("../boringssl-src/patches").join(patch); let status = Command::new("git") .current_dir(out_dir) - .arg("am") - .arg(source_dir.join("patches").join(patch).to_str().unwrap()) - .status()?; + .arg("apply") + .arg(fs::canonicalize(patch_path).unwrap().to_str().unwrap()) + .status() + .unwrap(); if !status.success() { return Err(io::Error::from(ErrorKind::Other)); @@ -101,12 +99,14 @@ fn build_boringssl>(dest: &P, options: &BoringSSLOptions) -> Path .define("CMAKE_CXX_COMPILER", "clang++") .pic(true) .cflag("-g") - .cxxflag("-g"); + .cxxflag("-g") + .define("CMAKE_BUILD_TYPE", "Release") + .define("OPENSSL_NO_BUF_FREELISTS", "1") + .define("OPENSSL_NO_ASM", "1"); if options.deterministic { - boring_conf - .define("FUZZ", "1") - .define("NO_FUZZER_MODE", "1"); + boring_conf.define("FUZZ", "1"); + boring_conf.define("NO_FUZZER_MODE", "1"); } if options.sancov { @@ -147,9 +147,7 @@ fn build_boringssl>(dest: &P, options: &BoringSSLOptions) -> Path .cxxflag("-fsanitize=address") .cxxflag("-shared-libsan") .cxxflag("-Wno-unused-command-line-argument") - .cxxflag(format!("-Wl,-rpath={}/lib/linux/", clang)) - .define("OPENSSL_NO_BUF_FREELISTS", "1") - .define("OPENSSL_NO_ASM", "1"); + .cxxflag(format!("-Wl,-rpath={}/lib/linux/", clang)); } boring_conf.build(); @@ -159,6 +157,9 @@ fn build_boringssl>(dest: &P, options: &BoringSSLOptions) -> Path pub fn build(options: &BoringSSLOptions) -> std::io::Result<()> { clone_boringssl(&options.source_dir, options)?; + // Patching CMakeList.txt to disable ASAN when using the fuzzer mode + let _ = patch_boringssl(&options.source_dir, "no_asan.patch").unwrap(); + let _ = build_boringssl(&options.out_dir, options); Ok(()) diff --git a/crates/boringssl-sys/build.rs b/crates/boringssl-sys/build.rs index 000132409..3cfdb284b 100644 --- a/crates/boringssl-sys/build.rs +++ b/crates/boringssl-sys/build.rs @@ -53,6 +53,11 @@ fn main() { .clang_arg("-I") .clang_arg(include_path.display().to_string()); + if cfg!(feature = "deterministic") { + // Exposes RAND_reset_for_fuzzing + builder = builder.clang_arg("-DBORINGSSL_UNSAFE_DETERMINISTIC_MODE=1"); + } + let headers = [ "aes.h", "asn1_mac.h", diff --git a/tlspuffin/Cargo.toml b/tlspuffin/Cargo.toml index f3475925a..b8f95b980 100644 --- a/tlspuffin/Cargo.toml +++ b/tlspuffin/Cargo.toml @@ -185,8 +185,8 @@ boringsslv1 = [ "boringssl-binding", "tls12-session-resumption", "tls13-session-resumption", - "transcript-extraction", - "client-authentication-transcript-extraction", + # "transcript-extraction", + # "client-authentication-transcript-extraction", "deterministic", ] @@ -224,7 +224,7 @@ tls13-session-resumption = [] # PUT supports session resumption through RFC5077 tls12-session-resumption = [] # PUT is determinisitic -deterministic = [] +deterministic = ["boringssl-sys?/deterministic"] # Require the PUT to implement the claim-interface.h claims = ["puffin/claims"] # Whether transcript extraction is supported. This is needed for simplified traces. diff --git a/tlspuffin/src/boringssl/bindings.rs b/tlspuffin/src/boringssl/bindings.rs deleted file mode 100644 index 4f6dd08c7..000000000 --- a/tlspuffin/src/boringssl/bindings.rs +++ /dev/null @@ -1,94 +0,0 @@ -use std::ffi::c_void; - -use boring::{ - error::ErrorStack, - pkey::Private, - rsa::Rsa, - ssl::{SslContext, SslContextBuilder, SslContextRef, SslRef}, -}; -use boringssl_sys::{SSL_CTX_set_options, SSL_clear, RSA, SSL, SSL_CTX}; -// use foreign_types_openssl::ForeignTypeRef; -use libc::{c_int, c_long, c_ulong}; - -const SSL_OP_ALLOW_NO_DHE_KEX: c_ulong = 0x00000400; -const SSL_CTRL_SET_TMP_RSA: c_int = 2; - -// unsafe fn SSL_CTX_set_tmp_rsa(ctx: *mut SSL_CTX, key: *mut RSA) -> c_long { -// SSL_CTX_ctrl(ctx, SSL_CTRL_SET_TMP_RSA, 0, key as *mut c_void) -// } - -// extern "C" { -// fn SSL_clear(ssl: *mut SSL) -> c_int; - -// #[cfg(not(feature = "openssl111-binding"))] -// fn SSL_CTX_set_tmp_rsa_callback( -// ctx: *mut SSL_CTX, -// ecdh: unsafe extern "C" fn(ssl: *mut SSL, is_export: c_int, keylength: c_int) -> *mut RSA, -// ); -// } - -// #[cfg(all( -// any(feature = "openssl101-binding", feature = "openssl102-binding"), -// not(feature = "openssl111-binding") -// ))] -// unsafe extern "C" fn raw_tmp_rsa(ssl: *mut SSL, is_export: c_int, keylength: c_int) -> *mut RSA -// where -// F: Fn(&mut SslRef, bool, u32) -> Result, ErrorStack> + 'static + Sync + Send, -// { -// let ssl = SslRef::from_ptr_mut(ssl); -// let callback = ssl -// .ssl_context() -// .ex_data(SslContext::new_ex_index::().unwrap()) -// .expect("BUG: tmp rsa callback missing") as *const F; - -// match (*callback)(ssl, is_export != 0, keylength as u32) { -// Ok(rsa_key) => { -// let ptr = rsa_key.as_ptr(); -// std::mem::forget(rsa_key); -// ptr -// } -// Err(e) => { -// e.put(); -// std::ptr::null_mut() -// } -// } -// } - -// #[cfg(all( -// any(feature = "openssl101-binding", feature = "openssl102-binding"), -// not(feature = "openssl111-binding") -// ))] -// pub fn set_tmp_rsa_callback(ctx: &mut SslContextBuilder, callback: F) -// where -// F: Fn(&mut SslRef, bool, u32) -> Result, ErrorStack> + 'static + Sync + Send, -// { -// unsafe { -// ctx.set_ex_data(SslContext::new_ex_index::().unwrap(), callback); -// SSL_CTX_set_tmp_rsa_callback(ctx.as_ptr(), raw_tmp_rsa::); -// } -// } - -// /// Sets the parameters to be used during ephemeral RSA key exchange. -// /// -// /// This corresponds to `SSL_CTX_set_tmp_rsa`. -// pub fn set_tmp_rsa(ctx: &SslContextBuilder, key: &Rsa) -> Result<(), ErrorStack> { -// unsafe { cvt(SSL_CTX_set_tmp_rsa(ctx.as_ptr(), key.as_ptr()) as c_int).map(|_| ()) } -// } - -// fn cvt(r: c_int) -> Result { -// if r <= 0 { -// Err(ErrorStack::get()) -// } else { -// Ok(r) -// } -// } - -// /// In TLSv1.3 allow a non-(ec)dhe based key exchange mode on resumption. -// /// This means that there will be no forward secrecy for the resumed session. -// pub fn set_allow_no_dhe_kex(ctx: &mut SslContextBuilder) { -// unsafe { SSL_CTX_set_options(ctx.as_ptr(), SSL_OP_ALLOW_NO_DHE_KEX) }; -// } - -pub fn clear(ssl: &mut SslRef) -> u32 { - unsafe { SSL_clear(&mut ssl) as u32 } -} diff --git a/tlspuffin/src/boringssl/deterministic.rs b/tlspuffin/src/boringssl/deterministic.rs index 9395f4a78..1447eeebc 100644 --- a/tlspuffin/src/boringssl/deterministic.rs +++ b/tlspuffin/src/boringssl/deterministic.rs @@ -1,35 +1,36 @@ -use std::os::raw::c_int; - +use boringssl_sys::RAND_reset_for_fuzzing; use log::warn; -#[cfg(feature = "deterministic")] -extern "C" { - fn make_openssl_deterministic(); - fn RAND_seed(buf: *mut u8, num: c_int); -} - -#[cfg(feature = "deterministic")] -pub fn set_openssl_deterministic() { - warn!("OpenSSL is no longer random!"); +/// Reset BoringSSL PRNG +/// BUG : it doesn't seems to work +pub fn reset_rand() { unsafe { - make_openssl_deterministic(); - let mut seed: [u8; 4] = 42u32.to_le().to_ne_bytes(); - let buf = seed.as_mut_ptr(); - RAND_seed(buf, 4); + RAND_reset_for_fuzzing(); } } #[cfg(test)] mod tests { - use openssl::rand::rand_bytes; + use crate::boringssl::deterministic::reset_rand; + use crate::put_registry::TLS_PUT_REGISTRY; + use crate::tls::seeds::{create_corpus, seed_client_attacker_boring}; + use crate::tls::trace_helper::TraceHelper; + use puffin::put::PutOptions; + use puffin::trace::{Action, InputAction, OutputAction, Step, Trace, TraceContext}; + use std::fmt::format; #[test] - #[cfg(feature = "openssl111-binding")] - fn test_openssl_no_randomness() { - use crate::openssl::deterministic::set_openssl_deterministic; - set_openssl_deterministic(); - let mut buf1 = [0; 2]; - rand_bytes(&mut buf1).unwrap(); - assert_eq!(buf1, [70, 100]); + fn test_boringssl_no_randomness_full() { + let trace = seed_client_attacker_boring.build_trace(); + let mut ctx1 = TraceContext::new(&TLS_PUT_REGISTRY, PutOptions::default()); + ctx1.set_deterministic(true); + trace.execute(&mut ctx1); + let mut ctx2 = TraceContext::new(&TLS_PUT_REGISTRY, PutOptions::default()); + ctx2.set_deterministic(true); + trace.execute(&mut ctx2); + + // println!("Left: {:#?}\n", ctx1); + // println!("Right: {:#?}\n", ctx2); + assert_eq!(ctx1, ctx2); } } diff --git a/tlspuffin/src/boringssl/mod.rs b/tlspuffin/src/boringssl/mod.rs index fec66c27e..95d78dc23 100644 --- a/tlspuffin/src/boringssl/mod.rs +++ b/tlspuffin/src/boringssl/mod.rs @@ -1,5 +1,4 @@ use std::{ - any::Any, cell::RefCell, fmt::{Debug, Formatter}, io, @@ -7,6 +6,8 @@ use std::{ rc::Rc, }; +use log::debug; + use boring::{ error::ErrorStack, pkey::{PKeyRef, Private}, @@ -46,9 +47,8 @@ use crate::{ }, }; -// mod bindings; -// #[cfg(feature = "deterministic")] -// mod deterministic; +#[cfg(feature = "deterministic")] +mod deterministic; mod util; /* @@ -99,6 +99,13 @@ pub fn new_boringssl_factory() -> Box> { BORINGSSL_PUT } + fn determinism_set_reseed(&self) -> () {} + + fn determinism_reseed(&self) -> () { + debug!("[Determinism] reseed"); + deterministic::reset_rand(); + } + fn version(&self) -> String { BoringSSL::version() } @@ -316,16 +323,15 @@ impl Put for BoringSSL { .contains("SSL negotiation finished successfully") } - fn set_deterministic(&mut self) -> Result<(), Error> { + fn determinism_reseed(&mut self) -> Result<(), Error> { #[cfg(feature = "deterministic")] { - // deterministic::set_openssl_deterministic(); Ok(()) } #[cfg(not(feature = "deterministic"))] { Err(Error::Agent( - "Unable to make OpenSSL deterministic!".to_string(), + "Unable to make BoringSSL deterministic!".to_string(), )) } } From 7cf65c7d33752c568f1547720900174690f72f01 Mon Sep 17 00:00:00 2001 From: Tom Gouville Date: Wed, 17 Jan 2024 10:39:53 +0100 Subject: [PATCH 20/63] adding git repo link to BoringSSLOptions --- crates/boringssl-src/src/lib.rs | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/crates/boringssl-src/src/lib.rs b/crates/boringssl-src/src/lib.rs index 74185662d..1657d1eb5 100644 --- a/crates/boringssl-src/src/lib.rs +++ b/crates/boringssl-src/src/lib.rs @@ -16,6 +16,7 @@ pub struct BoringSSLOptions { pub gcov_analysis: bool, pub llvm_cov_analysis: bool, + pub git_repo: String, pub git_ref: GitRef, pub out_dir: PathBuf, pub source_dir: PathBuf, @@ -64,13 +65,14 @@ fn clone_boringssl>(dest: &P, options: &BoringSSLOptions) -> std: .arg("1") .arg("--branch") .arg(&branch_name) - .arg("https://github.com/google/boringssl.git") + .arg(&options.git_repo) .arg(dest.as_ref().to_str().unwrap()) .status()?, GitRef::Commit(commit_id) => { Command::new("git") .arg("clone") - .arg("https://github.com/google/boringssl.git") + .arg("--filter=tree:0") + .arg(&options.git_repo) .arg(dest.as_ref().to_str().unwrap()) .status()?; Command::new("git") @@ -155,7 +157,7 @@ fn build_boringssl>(dest: &P, options: &BoringSSLOptions) -> Path } pub fn build(options: &BoringSSLOptions) -> std::io::Result<()> { - clone_boringssl(&options.source_dir, options)?; + clone_boringssl(&options.source_dir, options).unwrap(); // Patching CMakeList.txt to disable ASAN when using the fuzzer mode let _ = patch_boringssl(&options.source_dir, "no_asan.patch").unwrap(); From d531d5e382467c9fc2633d815d2392ec557ef4e3 Mon Sep 17 00:00:00 2001 From: Tom Gouville Date: Tue, 23 Jan 2024 13:32:24 +0100 Subject: [PATCH 21/63] adding patches to reset boringssl randomness --- crates/boringssl-src/patches/no_asan.patch | 2 +- crates/boringssl-src/patches/reset_drbg.patch | 42 +++++++++++++++++++ crates/boringssl-src/src/lib.rs | 5 +++ 3 files changed, 48 insertions(+), 1 deletion(-) create mode 100644 crates/boringssl-src/patches/reset_drbg.patch diff --git a/crates/boringssl-src/patches/no_asan.patch b/crates/boringssl-src/patches/no_asan.patch index 26c42b399..825c6577d 100644 --- a/crates/boringssl-src/patches/no_asan.patch +++ b/crates/boringssl-src/patches/no_asan.patch @@ -1,7 +1,7 @@ From c1d43d464d33140729f2524afad1d1bcf63267f7 Mon Sep 17 00:00:00 2001 From: Tom Gouville Date: Tue, 9 Jan 2024 17:55:47 +0100 -Subject: [PATCH] oui +Subject: [PATCH] disable asan flags when building in FUZZ mode --- CMakeLists.txt | 2 -- diff --git a/crates/boringssl-src/patches/reset_drbg.patch b/crates/boringssl-src/patches/reset_drbg.patch new file mode 100644 index 000000000..31e164388 --- /dev/null +++ b/crates/boringssl-src/patches/reset_drbg.patch @@ -0,0 +1,42 @@ +From 3e2702713929cc2acae7bc38c68973679492584a Mon Sep 17 00:00:00 2001 +From: Tom Gouville +Date: Tue, 23 Jan 2024 11:57:36 +0100 +Subject: [PATCH] reset DRBG + +--- + crypto/rand_extra/deterministic.c | 19 ++++++++++++++++++- + 1 file changed, 18 insertions(+), 1 deletion(-) + +diff --git a/crypto/rand_extra/deterministic.c b/crypto/rand_extra/deterministic.c +index d1d582b07..ff42db9d3 100644 +--- a/crypto/rand_extra/deterministic.c ++++ b/crypto/rand_extra/deterministic.c +@@ -33,7 +33,24 @@ + static uint64_t g_num_calls = 0; + static CRYPTO_MUTEX g_num_calls_lock = CRYPTO_MUTEX_INIT; + +-void RAND_reset_for_fuzzing(void) { g_num_calls = 0; } ++static void rand_thread_state_free(void *state_in) { ++ struct rand_thread_state *state = state_in; ++ ++ if (state_in == NULL) { ++ return; ++ } ++ ++ OPENSSL_free(state); ++} ++ ++void RAND_reset_for_fuzzing(void) { ++ g_num_calls = 0; ++ struct rand_thread_state *state = ++ CRYPTO_get_thread_local(OPENSSL_THREAD_LOCAL_RAND); ++ rand_thread_state_free(state); ++ ++ CRYPTO_set_thread_local(OPENSSL_THREAD_LOCAL_RAND, NULL, NULL); ++} + + void CRYPTO_sysrand(uint8_t *out, size_t requested) { + static const uint8_t kZeroKey[32]; +-- +2.43.0 + diff --git a/crates/boringssl-src/src/lib.rs b/crates/boringssl-src/src/lib.rs index 1657d1eb5..15109e8f8 100644 --- a/crates/boringssl-src/src/lib.rs +++ b/crates/boringssl-src/src/lib.rs @@ -162,6 +162,11 @@ pub fn build(options: &BoringSSLOptions) -> std::io::Result<()> { // Patching CMakeList.txt to disable ASAN when using the fuzzer mode let _ = patch_boringssl(&options.source_dir, "no_asan.patch").unwrap(); + if options.deterministic { + // Patching boringssl to reset the DRBG + let _ = patch_boringssl(&options.source_dir, "reset_drbg.patch").unwrap(); + } + let _ = build_boringssl(&options.out_dir, options); Ok(()) From 126a71d51bc66c4455f631999edf96eccf002b1e Mon Sep 17 00:00:00 2001 From: Tom Gouville Date: Tue, 23 Jan 2024 13:35:50 +0100 Subject: [PATCH 22/63] forgot adding boring repo url --- crates/boringssl-sys/build.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/crates/boringssl-sys/build.rs b/crates/boringssl-sys/build.rs index 3cfdb284b..aac29f9af 100644 --- a/crates/boringssl-sys/build.rs +++ b/crates/boringssl-sys/build.rs @@ -10,6 +10,7 @@ fn main() { } else { GitRef::Branch(String::from("master")) }; + let repo = "https://github.com/google/boringssl.git".into(); let source_dir = PathBuf::from(env::var("OUT_DIR").unwrap()).join("boringssl"); let out_dir = PathBuf::from(env::var("OUT_DIR").unwrap()).join("boring"); build(&BoringSSLOptions { @@ -18,6 +19,7 @@ fn main() { gcov_analysis: cfg!(feature = "gcov_analysis"), llvm_cov_analysis: cfg!(feature = "llvm_cov_analysis"), deterministic: cfg!(feature = "deterministic"), + git_repo: repo, git_ref, out_dir: out_dir.clone(), source_dir: source_dir.clone(), From e43d6c4b4564a2fb5663f1af26332f1d8cd3d767 Mon Sep 17 00:00:00 2001 From: Tom Gouville Date: Thu, 1 Feb 2024 14:39:44 +0100 Subject: [PATCH 23/63] adding transcrit extraction + fixing bugs --- .../patches/extract_transcript.patch | 60 ++++++++ crates/boringssl-src/src/lib.rs | 1 + crates/boringssl-src/src/main.rs | 1 + crates/boringssl-sys/build.rs | 1 + tlspuffin/src/boringssl/deterministic.rs | 2 - tlspuffin/src/boringssl/mod.rs | 143 ++++++++++++++---- tlspuffin/src/boringssl/transcript.rs | 33 ++++ tlspuffin/src/boringssl/util.rs | 56 +------ 8 files changed, 209 insertions(+), 88 deletions(-) create mode 100644 crates/boringssl-src/patches/extract_transcript.patch create mode 100644 tlspuffin/src/boringssl/transcript.rs diff --git a/crates/boringssl-src/patches/extract_transcript.patch b/crates/boringssl-src/patches/extract_transcript.patch new file mode 100644 index 000000000..84f68dd48 --- /dev/null +++ b/crates/boringssl-src/patches/extract_transcript.patch @@ -0,0 +1,60 @@ +From f87436455c195b7010de117f766377551ecba2c9 Mon Sep 17 00:00:00 2001 +From: Tom Gouville +Date: Thu, 1 Feb 2024 10:29:36 +0100 +Subject: [PATCH] transcript extraction + +--- + include/openssl/ssl.h | 3 +++ + ssl/ssl_lib.cc | 22 ++++++++++++++++++++++ + 2 files changed, 25 insertions(+) + +diff --git a/include/openssl/ssl.h b/include/openssl/ssl.h +index 2e19253c2..54346a8a9 100644 +--- a/include/openssl/ssl.h ++++ b/include/openssl/ssl.h +@@ -170,6 +170,9 @@ extern "C" { + // SSL implementation. + + ++OPENSSL_EXPORT int PUFFIN_extract_transcript(SSL* ssl, uint8_t* out, size_t* out_len); ++OPENSSL_EXPORT const char* PUFFIN_get_server_handshake_state(SSL *ssl); ++ + // SSL contexts. + // + // |SSL_CTX| objects manage shared state and configuration between multiple TLS +diff --git a/ssl/ssl_lib.cc b/ssl/ssl_lib.cc +index 58b68e675..ee9bc76fe 100644 +--- a/ssl/ssl_lib.cc ++++ b/ssl/ssl_lib.cc +@@ -499,6 +499,28 @@ BSSL_NAMESPACE_END + + using namespace bssl; + ++int PUFFIN_extract_transcript(SSL *ssl, uint8_t *out, size_t *out_len) { ++ SSL_HANDSHAKE *hs = ssl->s3->hs.get(); ++ ++ if (hs == NULL) { ++ return 0; ++ } else { ++ hs->transcript.GetHash(out, out_len); ++ } ++ ++ return 1; ++} ++ ++ ++const char *PUFFIN_get_server_handshake_state(SSL *ssl) { ++ SSL_HANDSHAKE *hs = ssl->s3->hs.get(); ++ if (hs == NULL) { ++ return "NULL HANDSHAKE"; ++ } ++ return tls13_server_handshake_state(hs); ++} ++ ++ + int SSL_library_init(void) { + CRYPTO_library_init(); + return 1; +-- +2.43.0 + diff --git a/crates/boringssl-src/src/lib.rs b/crates/boringssl-src/src/lib.rs index 15109e8f8..938aadfe3 100644 --- a/crates/boringssl-src/src/lib.rs +++ b/crates/boringssl-src/src/lib.rs @@ -161,6 +161,7 @@ pub fn build(options: &BoringSSLOptions) -> std::io::Result<()> { // Patching CMakeList.txt to disable ASAN when using the fuzzer mode let _ = patch_boringssl(&options.source_dir, "no_asan.patch").unwrap(); + let _ = patch_boringssl(&options.source_dir, "extract_transcript.patch").unwrap(); if options.deterministic { // Patching boringssl to reset the DRBG diff --git a/crates/boringssl-src/src/main.rs b/crates/boringssl-src/src/main.rs index e3fa43989..76741be24 100644 --- a/crates/boringssl-src/src/main.rs +++ b/crates/boringssl-src/src/main.rs @@ -21,6 +21,7 @@ fn main() { asan: false, sancov: true, git_ref: GitRef::Branch("master".to_string()), + git_repo: "https://github.com/google/boringssl".into(), out_dir, source_dir: PathBuf::from(env::var("CARGO_MANIFEST_DIR").unwrap()).join("boringssl"), }) diff --git a/crates/boringssl-sys/build.rs b/crates/boringssl-sys/build.rs index aac29f9af..851d47756 100644 --- a/crates/boringssl-sys/build.rs +++ b/crates/boringssl-sys/build.rs @@ -91,6 +91,7 @@ fn main() { "srtp.h", "trust_token.h", "x509v3.h", + "ssl.h", ]; for header in &headers { builder = builder.header(include_path.join("openssl").join(header).to_str().unwrap()); diff --git a/tlspuffin/src/boringssl/deterministic.rs b/tlspuffin/src/boringssl/deterministic.rs index 1447eeebc..5b715a6ba 100644 --- a/tlspuffin/src/boringssl/deterministic.rs +++ b/tlspuffin/src/boringssl/deterministic.rs @@ -1,8 +1,6 @@ use boringssl_sys::RAND_reset_for_fuzzing; -use log::warn; /// Reset BoringSSL PRNG -/// BUG : it doesn't seems to work pub fn reset_rand() { unsafe { RAND_reset_for_fuzzing(); diff --git a/tlspuffin/src/boringssl/mod.rs b/tlspuffin/src/boringssl/mod.rs index 95d78dc23..c40c024e7 100644 --- a/tlspuffin/src/boringssl/mod.rs +++ b/tlspuffin/src/boringssl/mod.rs @@ -1,40 +1,33 @@ -use std::{ - cell::RefCell, - fmt::{Debug, Formatter}, - io, - io::ErrorKind, - rc::Rc, -}; +use std::{cell::RefCell, io::ErrorKind, rc::Rc}; +use foreign_types::ForeignTypeRef; use log::debug; use boring::{ error::ErrorStack, - pkey::{PKeyRef, Private}, - ssl::{Ssl, SslContext, SslMethod, SslOptions, SslStream, SslVerifyMode}, - stack::Stack, - x509::{ - store::{X509Store, X509StoreBuilder}, - X509Ref, X509StoreContext, X509, - }, + ex_data::Index, + ssl::{Ssl, SslContext, SslMethod, SslRef, SslStream, SslVerifyMode}, + x509::{store::X509StoreBuilder, X509}, }; -use log::{info, warn}; +use log::info; use puffin::{ agent::{AgentDescriptor, AgentName, AgentType, TLSVersion}, error::Error, protocol::MessageResult, - put::{Put, PutDescriptor, PutName}, + put::{Put, PutName}, put_registry::Factory, stream::{MemoryStream, Stream}, trace::TraceContext, }; use smallvec::SmallVec; +use boringssl_sys::ssl_st; + use crate::{ boringssl::util::{set_max_protocol_version, static_rsa_cert}, claims::{ - ClaimData, ClaimDataMessage, ClaimDataTranscript, ClientHello, Finished, TlsClaim, - TlsTranscript, TranscriptCertificate, TranscriptClientFinished, TranscriptClientHello, + ClaimData, ClaimDataMessage, ClaimDataTranscript, Finished, TlsClaim, TlsTranscript, + TranscriptCertificate, TranscriptClientFinished, TranscriptClientHello, TranscriptPartialClientHello, TranscriptServerFinished, TranscriptServerHello, }, protocol::TLSProtocolBehavior, @@ -49,14 +42,11 @@ use crate::{ #[cfg(feature = "deterministic")] mod deterministic; +mod transcript; mod util; -/* - Change openssl version: - cargo clean -p openssl-src - cd openssl-src/openssl - git checkout OpenSSL_1_1_1j -*/ +use std::ops::Deref; +use transcript::extract_current_transcript; pub fn new_boringssl_factory() -> Box> { struct BoringSSLFactory; @@ -99,10 +89,13 @@ pub fn new_boringssl_factory() -> Box> { BORINGSSL_PUT } - fn determinism_set_reseed(&self) -> () {} + fn determinism_set_reseed(&self) -> () { + debug!("[Determinism] BoringSSL set (already done at compile time) and reseed RAND"); + deterministic::reset_rand(); + } fn determinism_reseed(&self) -> () { - debug!("[Determinism] reseed"); + debug!("[Determinism] reseed BoringSSL"); deterministic::reset_rand(); } @@ -144,7 +137,10 @@ impl Stream for BoringSSL { } } -fn to_claim_data(protocol_version: TLSVersion, claim: security_claims::Claim) -> Option { +fn _to_claim_data( + protocol_version: TLSVersion, + claim: security_claims::Claim, +) -> Option { match claim.typ { // Transcripts security_claims::ClaimType::CLAIM_TRANSCRIPT_CH => Some(ClaimData::Transcript( @@ -242,7 +238,7 @@ fn to_claim_data(protocol_version: TLSVersion, claim: security_claims::Claim) -> } impl Put for BoringSSL { - fn progress(&mut self, _agent_name: &AgentName) -> Result<(), Error> { + fn progress(&mut self, agent_name: &AgentName) -> Result<(), Error> { let result = if self.is_state_successful() { // Trigger another read let mut vec: Vec = Vec::from([1; 128]); @@ -253,11 +249,13 @@ impl Put for BoringSSL { maybe_error.into() }; + self.set_info_callback(Self::create_info_callback(agent_name.clone(), &self.config)) + .expect("Failed to set info_callback to extract transcript"); + result } - fn reset(&mut self, agent_name: AgentName) -> Result<(), Error> { - // bindings::clear(&mut self.stream.ssl()); // FIXME: Add non-clear method like in wolfssl + fn reset(&mut self, _agent_name: AgentName) -> Result<(), Error> { self.stream.ssl_mut().clear(); Ok(()) } @@ -348,7 +346,7 @@ impl Put for BoringSSL { impl BoringSSL { fn new(config: TlsPutConfig) -> Result { let agent_descriptor = &config.descriptor; - let mut ssl = match agent_descriptor.typ { + let ssl = match agent_descriptor.typ { AgentType::Server => Self::create_server(agent_descriptor)?, AgentType::Client => Self::create_client(agent_descriptor)?, }; @@ -363,6 +361,8 @@ impl BoringSSL { #[cfg(feature = "claims")] boringssl.register_claimer(agent_name); + // boringssl.set_info_callback(Self::create_info_callback(agent_name, &boringssl.config)); + Ok(boringssl) } @@ -429,6 +429,64 @@ impl BoringSSL { Ok(ssl) } + + /// Set the info_callback of BoringSSL + /// + /// Here we use a intermediate callback, `boring_info_callback`, to call the + /// `callback` function `callback` is stored in the boringssl ex_data to be + /// retrieved and executed by `boring_info_callback` + fn set_info_callback(&mut self, callback: F) -> Result<(), ErrorStack> + where + F: Fn(&mut SslRef, i32) + 'static, + { + unsafe { + let ssl = self.stream.ssl_mut(); + ssl.set_ex_data(Index::from_raw(0), callback); + let ssl_ptr = ssl.as_ptr(); + boringssl_sys::SSL_set_info_callback(ssl_ptr, Some(boring_info_callback::)); + + Ok(()) + } + } + + /// This callback gets the actual hash transcript of the SSL handshake and + /// add it to the claims + fn create_info_callback( + agent_name: AgentName, + config: &TlsPutConfig, + ) -> impl Fn(&mut SslRef, i32) { + let origin = config.descriptor.typ; + let protocol_version = config.descriptor.tls_version; + let claims = config.claims.clone(); + + move |ssl: &mut SslRef, _info_type: i32| { + if let Some((transcript, status)) = extract_current_transcript(ssl) { + println!( + "JE SUIS DANS LE CALLBACK à l'étape '{}' et le transcript est {:02x?}", + status, transcript.0 + ); + + let claim = match status.as_str() { + "TLS 1.3 server read_client_finished" => Some(ClaimData::Transcript( + ClaimDataTranscript::ClientFinished(TranscriptClientFinished(transcript)), + )), + "TLS 1.3 server done" => Some(ClaimData::Transcript( + ClaimDataTranscript::ServerFinished(TranscriptServerFinished(transcript)), + )), + _ => None, + }; + + if let Some(data) = claim { + claims.deref_borrow_mut().claim_sized(TlsClaim { + agent_name, + origin, + protocol_version, + data, + }); + } + } + } + } } pub enum MaybeError { @@ -436,6 +494,29 @@ pub enum MaybeError { Err(Error), } +/// This callback uses boringssl ex_data to get a pointer to the real callback +/// and to execute it. +/// +/// This allows to use a closure as a callback since `SSL_set_info_callback` +/// accepts only `unsafe extern "C"` functions +pub unsafe extern "C" fn boring_info_callback(ssl: *const ssl_st, info_type: i32, _value: i32) +where + F: Fn(&mut SslRef, i32) + 'static, +{ + let ssl = SslRef::from_ptr_mut(ssl as *mut ssl_st); + + // Getting the callback from ex_data index 0 + let callback = { + let callback = ssl + .ex_data::(Index::from_raw(0)) + .expect("BUG: missing info_callback"); + + callback.deref() as *const F + }; + + (*callback)(ssl, info_type); +} + impl From> for MaybeError { fn from(result: Result) -> Self { if let Err(ssl_error) = result { diff --git a/tlspuffin/src/boringssl/transcript.rs b/tlspuffin/src/boringssl/transcript.rs new file mode 100644 index 000000000..67a18f635 --- /dev/null +++ b/tlspuffin/src/boringssl/transcript.rs @@ -0,0 +1,33 @@ +use boring::ssl::SslRef; +use boringssl_sys as boringssl; +use core::ffi::{c_char, CStr}; +use foreign_types::ForeignTypeRef; + +use crate::claims::TlsTranscript; + +/// Extract the current transcript hash and actual state from boringssl during +/// the handshake +pub fn extract_current_transcript(ssl: &SslRef) -> Option<(TlsTranscript, String)> { + let mut target: [u8; 64] = [0; 64]; + let mut s: usize = 0; + unsafe { + let ssl = ssl.as_ptr(); + if boringssl::PUFFIN_extract_transcript(ssl, target.as_mut_ptr(), &mut s as *mut usize) == 0 + { + return None; + } + + let status: *const c_char = boringssl::PUFFIN_get_server_handshake_state(ssl); + let status_str = ptr_to_string(status); + + Some((TlsTranscript(target, s as i32), status_str)) + } +} + +fn ptr_to_string(ptr: *const c_char) -> String { + // Convert the *const c_char pointer to a *const u8 pointer + let ptr_u8: *const u8 = ptr as *const u8; + // Dereference the *const u8 pointer to get a slice of bytes + let bytes = unsafe { std::slice::from_raw_parts(ptr_u8, CStr::from_ptr(ptr).to_bytes().len()) }; + String::from_utf8(bytes.to_vec()).unwrap() +} diff --git a/tlspuffin/src/boringssl/util.rs b/tlspuffin/src/boringssl/util.rs index 4356e7ae6..3727ad8eb 100644 --- a/tlspuffin/src/boringssl/util.rs +++ b/tlspuffin/src/boringssl/util.rs @@ -1,65 +1,11 @@ use boring::{ - asn1::Asn1Time, - bn::{BigNum, MsbOption}, error::ErrorStack, - hash::MessageDigest, pkey::{PKey, Private}, ssl::{SslContextBuilder, SslVersion}, - x509::{ - extension::{BasicConstraints, KeyUsage, SubjectKeyIdentifier}, - X509NameBuilder, X509, - }, + x509::X509, }; use puffin::agent::TLSVersion; -use crate::static_certs::{ALICE_CERT, ALICE_PRIVATE_KEY}; - -// FIXME: remove or use -pub fn generate_cert() -> Result<(X509, PKey), ErrorStack> { - let rsa = boring::rsa::Rsa::generate(2048)?; - let pkey = PKey::from_rsa(rsa)?; - - let mut x509_name = X509NameBuilder::new()?; - x509_name.append_entry_by_text("C", "US")?; - x509_name.append_entry_by_text("ST", "TX")?; - x509_name.append_entry_by_text("O", "Some CA organization")?; - x509_name.append_entry_by_text("CN", "ca test")?; - let x509_name = x509_name.build(); - let mut cert_builder = X509::builder()?; - cert_builder.set_version(2)?; - let serial_number = { - let mut serial = BigNum::new()?; - serial.rand(159, MsbOption::MAYBE_ZERO, false)?; - serial.to_asn1_integer() - }?; - cert_builder.set_serial_number(&serial_number)?; - cert_builder.set_subject_name(&x509_name)?; - cert_builder.set_issuer_name(&x509_name)?; - cert_builder.set_pubkey(&pkey)?; - let not_before = Asn1Time::days_from_now(0)?; - cert_builder.set_not_before(¬_before)?; - let not_after = Asn1Time::days_from_now(365)?; - cert_builder.set_not_after(¬_after)?; - - let extension = BasicConstraints::new().critical().ca().build()?; - cert_builder.append_extension(extension)?; - cert_builder.append_extension( - KeyUsage::new() - .critical() - .key_cert_sign() - .crl_sign() - .build()?, - )?; - - let subject_key_identifier = - SubjectKeyIdentifier::new().build(&cert_builder.x509v3_context(None, None))?; - cert_builder.append_extension(subject_key_identifier)?; - - cert_builder.sign(&pkey, MessageDigest::sha256())?; - let cert = cert_builder.build(); - Ok((cert, pkey)) -} - pub fn static_rsa_cert(key: &[u8], cert: &[u8]) -> Result<(X509, PKey), ErrorStack> { let rsa = boring::rsa::Rsa::private_key_from_pem(key)?; let pkey = PKey::from_rsa(rsa)?; From 07bc293dca6a1344d557802cea551650db987314 Mon Sep 17 00:00:00 2001 From: Tom Gouville Date: Thu, 1 Feb 2024 14:55:21 +0100 Subject: [PATCH 24/63] removing debug print --- tlspuffin/src/boringssl/mod.rs | 5 ----- 1 file changed, 5 deletions(-) diff --git a/tlspuffin/src/boringssl/mod.rs b/tlspuffin/src/boringssl/mod.rs index c40c024e7..a8b32a805 100644 --- a/tlspuffin/src/boringssl/mod.rs +++ b/tlspuffin/src/boringssl/mod.rs @@ -461,11 +461,6 @@ impl BoringSSL { move |ssl: &mut SslRef, _info_type: i32| { if let Some((transcript, status)) = extract_current_transcript(ssl) { - println!( - "JE SUIS DANS LE CALLBACK à l'étape '{}' et le transcript est {:02x?}", - status, transcript.0 - ); - let claim = match status.as_str() { "TLS 1.3 server read_client_finished" => Some(ClaimData::Transcript( ClaimDataTranscript::ClientFinished(TranscriptClientFinished(transcript)), From 7d97f05e9437cd13d6509d0c79161e9f932e883b Mon Sep 17 00:00:00 2001 From: Tom Gouville Date: Mon, 5 Feb 2024 14:46:49 +0100 Subject: [PATCH 25/63] improving boringssl transcript extraction perfs --- .../patches/extract_transcript.patch | 15 +-- tlspuffin/Cargo.toml | 6 +- tlspuffin/src/boringssl/mod.rs | 108 ++++++++++-------- tlspuffin/src/boringssl/transcript.rs | 18 +-- 4 files changed, 67 insertions(+), 80 deletions(-) diff --git a/crates/boringssl-src/patches/extract_transcript.patch b/crates/boringssl-src/patches/extract_transcript.patch index 84f68dd48..36edb5110 100644 --- a/crates/boringssl-src/patches/extract_transcript.patch +++ b/crates/boringssl-src/patches/extract_transcript.patch @@ -12,12 +12,11 @@ diff --git a/include/openssl/ssl.h b/include/openssl/ssl.h index 2e19253c2..54346a8a9 100644 --- a/include/openssl/ssl.h +++ b/include/openssl/ssl.h -@@ -170,6 +170,9 @@ extern "C" { +@@ -170,6 +170,8 @@ extern "C" { // SSL implementation. +OPENSSL_EXPORT int PUFFIN_extract_transcript(SSL* ssl, uint8_t* out, size_t* out_len); -+OPENSSL_EXPORT const char* PUFFIN_get_server_handshake_state(SSL *ssl); + // SSL contexts. // @@ -26,7 +25,7 @@ diff --git a/ssl/ssl_lib.cc b/ssl/ssl_lib.cc index 58b68e675..ee9bc76fe 100644 --- a/ssl/ssl_lib.cc +++ b/ssl/ssl_lib.cc -@@ -499,6 +499,28 @@ BSSL_NAMESPACE_END +@@ -499,6 +499,18 @@ BSSL_NAMESPACE_END using namespace bssl; @@ -41,16 +40,6 @@ index 58b68e675..ee9bc76fe 100644 + + return 1; +} -+ -+ -+const char *PUFFIN_get_server_handshake_state(SSL *ssl) { -+ SSL_HANDSHAKE *hs = ssl->s3->hs.get(); -+ if (hs == NULL) { -+ return "NULL HANDSHAKE"; -+ } -+ return tls13_server_handshake_state(hs); -+} -+ + int SSL_library_init(void) { CRYPTO_library_init(); diff --git a/tlspuffin/Cargo.toml b/tlspuffin/Cargo.toml index b8f95b980..921d1b791 100644 --- a/tlspuffin/Cargo.toml +++ b/tlspuffin/Cargo.toml @@ -176,6 +176,7 @@ boringsslmaster = [ "transcript-extraction", "client-authentication-transcript-extraction", "deterministic", + "claims" ] boringsslv1 = [ @@ -185,8 +186,9 @@ boringsslv1 = [ "boringssl-binding", "tls12-session-resumption", "tls13-session-resumption", - # "transcript-extraction", - # "client-authentication-transcript-extraction", + "transcript-extraction", + "claims", + "client-authentication-transcript-extraction", "deterministic", ] diff --git a/tlspuffin/src/boringssl/mod.rs b/tlspuffin/src/boringssl/mod.rs index a8b32a805..7a6be6622 100644 --- a/tlspuffin/src/boringssl/mod.rs +++ b/tlspuffin/src/boringssl/mod.rs @@ -1,4 +1,4 @@ -use std::{cell::RefCell, io::ErrorKind, rc::Rc}; +use std::{cell::RefCell, fmt::Pointer, io::ErrorKind, rc::Rc}; use foreign_types::ForeignTypeRef; use log::debug; @@ -249,9 +249,6 @@ impl Put for BoringSSL { maybe_error.into() }; - self.set_info_callback(Self::create_info_callback(agent_name.clone(), &self.config)) - .expect("Failed to set info_callback to extract transcript"); - result } @@ -266,35 +263,38 @@ impl Put for BoringSSL { #[cfg(feature = "claims")] fn register_claimer(&mut self, agent_name: AgentName) { - unsafe { - use foreign_types_openssl::ForeignTypeRef; - - let claims = self.config.claims.clone(); - let protocol_version = self.config.descriptor.tls_version; - let origin = self.config.descriptor.typ; - - security_claims::register_claimer( - self.stream.ssl().as_ptr().cast(), - move |claim: security_claims::Claim| { - if let Some(data) = to_claim_data(protocol_version, claim) { - claims.deref_borrow_mut().claim_sized(TlsClaim { - agent_name, - origin, - protocol_version, - data, - }) - } - }, - ); - } + self.set_info_callback(Self::create_info_callback(agent_name.clone(), &self.config)) + .expect("Failed to set info_callback to extract transcript"); + + // unsafe { + // use foreign_types_openssl::ForeignTypeRef; + + // let claims = self.config.claims.clone(); + // let protocol_version = self.config.descriptor.tls_version; + // let origin = self.config.descriptor.typ; + + // security_claims::register_claimer( + // self.stream.ssl().as_ptr().cast(), + // move |claim: security_claims::Claim| { + // if let Some(data) = to_claim_data(protocol_version, claim) { + // claims.deref_borrow_mut().claim_sized(TlsClaim { + // agent_name, + // origin, + // protocol_version, + // data, + // }) + // } + // }, + // ); + // } } #[cfg(feature = "claims")] fn deregister_claimer(&mut self) { - unsafe { - use foreign_types_openssl::ForeignTypeRef; - security_claims::deregister_claimer(self.stream.ssl().as_ptr().cast()); - } + // unsafe { + // use foreign_types_openssl::ForeignTypeRef; + // security_claims::deregister_claimer(self.stream.ssl().as_ptr().cast()); + // } } #[allow(unused_variables)] @@ -304,6 +304,7 @@ impl Put for BoringSSL { self.deregister_claimer(); self.register_claimer(agent_name); } + self.register_claimer(agent_name); Ok(()) } @@ -353,7 +354,6 @@ impl BoringSSL { let stream = SslStream::new(ssl, MemoryStream::new(MessageDeframer::new()))?; - #[cfg(feature = "claims")] let agent_name = agent_descriptor.name; let mut boringssl = BoringSSL { config, stream }; @@ -361,8 +361,6 @@ impl BoringSSL { #[cfg(feature = "claims")] boringssl.register_claimer(agent_name); - // boringssl.set_info_callback(Self::create_info_callback(agent_name, &boringssl.config)); - Ok(boringssl) } @@ -460,25 +458,29 @@ impl BoringSSL { let claims = config.claims.clone(); move |ssl: &mut SslRef, _info_type: i32| { - if let Some((transcript, status)) = extract_current_transcript(ssl) { - let claim = match status.as_str() { - "TLS 1.3 server read_client_finished" => Some(ClaimData::Transcript( - ClaimDataTranscript::ClientFinished(TranscriptClientFinished(transcript)), - )), - "TLS 1.3 server done" => Some(ClaimData::Transcript( - ClaimDataTranscript::ServerFinished(TranscriptServerFinished(transcript)), - )), - _ => None, - }; - - if let Some(data) = claim { - claims.deref_borrow_mut().claim_sized(TlsClaim { - agent_name, - origin, - protocol_version, - data, - }); + let claim = match ssl.state_string_long() { + "TLS 1.3 server read_client_finished" => { + extract_current_transcript(ssl).map_or(None, |t| { + Some(ClaimData::Transcript(ClaimDataTranscript::ClientFinished( + TranscriptClientFinished(t), + ))) + }) } + "TLS 1.3 server done" => extract_current_transcript(ssl).map_or(None, |t| { + Some(ClaimData::Transcript(ClaimDataTranscript::ServerFinished( + TranscriptServerFinished(t), + ))) + }), + _ => None, + }; + + if let Some(data) = claim { + claims.deref_borrow_mut().claim_sized(TlsClaim { + agent_name, + origin, + protocol_version, + data, + }); } } } @@ -498,6 +500,12 @@ pub unsafe extern "C" fn boring_info_callback(ssl: *const ssl_st, info_type: where F: Fn(&mut SslRef, i32) + 'static, { + // Exit the callback if it's called from a state that doesn's increase the knowledge + // Here `SSL_CB_ACCEPT_LOOP` seems to allow to get all the transcript changes + // All the states types for the callback are defined in `include/openssl/ssl.h` in BoringSSL + if !(info_type == boringssl_sys::SSL_CB_ACCEPT_LOOP) { + return; + } let ssl = SslRef::from_ptr_mut(ssl as *mut ssl_st); // Getting the callback from ex_data index 0 diff --git a/tlspuffin/src/boringssl/transcript.rs b/tlspuffin/src/boringssl/transcript.rs index 67a18f635..afe46127b 100644 --- a/tlspuffin/src/boringssl/transcript.rs +++ b/tlspuffin/src/boringssl/transcript.rs @@ -5,9 +5,8 @@ use foreign_types::ForeignTypeRef; use crate::claims::TlsTranscript; -/// Extract the current transcript hash and actual state from boringssl during -/// the handshake -pub fn extract_current_transcript(ssl: &SslRef) -> Option<(TlsTranscript, String)> { +/// Extract the current transcript hash from boringssl during the handshake +pub fn extract_current_transcript(ssl: &SslRef) -> Option { let mut target: [u8; 64] = [0; 64]; let mut s: usize = 0; unsafe { @@ -17,17 +16,6 @@ pub fn extract_current_transcript(ssl: &SslRef) -> Option<(TlsTranscript, String return None; } - let status: *const c_char = boringssl::PUFFIN_get_server_handshake_state(ssl); - let status_str = ptr_to_string(status); - - Some((TlsTranscript(target, s as i32), status_str)) + Some(TlsTranscript(target, s as i32)) } } - -fn ptr_to_string(ptr: *const c_char) -> String { - // Convert the *const c_char pointer to a *const u8 pointer - let ptr_u8: *const u8 = ptr as *const u8; - // Dereference the *const u8 pointer to get a slice of bytes - let bytes = unsafe { std::slice::from_raw_parts(ptr_u8, CStr::from_ptr(ptr).to_bytes().len()) }; - String::from_utf8(bytes.to_vec()).unwrap() -} From 8ed0be4f9becece7953009c07b593bdd5e09f1ea Mon Sep 17 00:00:00 2001 From: Tom Gouville Date: Sun, 18 Feb 2024 18:43:00 +0100 Subject: [PATCH 26/63] working transcript extraction --- tlspuffin/src/boringssl/deterministic.rs | 10 +- tlspuffin/src/boringssl/mod.rs | 214 ++++++----------------- 2 files changed, 58 insertions(+), 166 deletions(-) diff --git a/tlspuffin/src/boringssl/deterministic.rs b/tlspuffin/src/boringssl/deterministic.rs index 5b715a6ba..24cd10627 100644 --- a/tlspuffin/src/boringssl/deterministic.rs +++ b/tlspuffin/src/boringssl/deterministic.rs @@ -11,7 +11,7 @@ pub fn reset_rand() { mod tests { use crate::boringssl::deterministic::reset_rand; use crate::put_registry::TLS_PUT_REGISTRY; - use crate::tls::seeds::{create_corpus, seed_client_attacker_boring}; + use crate::tls::seeds::{create_corpus, seed_client_attacker_full_boring}; use crate::tls::trace_helper::TraceHelper; use puffin::put::PutOptions; use puffin::trace::{Action, InputAction, OutputAction, Step, Trace, TraceContext}; @@ -19,16 +19,14 @@ mod tests { #[test] fn test_boringssl_no_randomness_full() { - let trace = seed_client_attacker_boring.build_trace(); + let trace = seed_client_attacker_full_boring.build_trace(); let mut ctx1 = TraceContext::new(&TLS_PUT_REGISTRY, PutOptions::default()); ctx1.set_deterministic(true); - trace.execute(&mut ctx1); + let _ = trace.execute(&mut ctx1); let mut ctx2 = TraceContext::new(&TLS_PUT_REGISTRY, PutOptions::default()); ctx2.set_deterministic(true); - trace.execute(&mut ctx2); + let _ = trace.execute(&mut ctx2); - // println!("Left: {:#?}\n", ctx1); - // println!("Right: {:#?}\n", ctx2); assert_eq!(ctx1, ctx2); } } diff --git a/tlspuffin/src/boringssl/mod.rs b/tlspuffin/src/boringssl/mod.rs index 7a6be6622..d7d77d93a 100644 --- a/tlspuffin/src/boringssl/mod.rs +++ b/tlspuffin/src/boringssl/mod.rs @@ -1,7 +1,7 @@ -use std::{cell::RefCell, fmt::Pointer, io::ErrorKind, rc::Rc}; +use std::{borrow::Borrow, cell::RefCell, fmt::Pointer, io::ErrorKind, rc::Rc}; use foreign_types::ForeignTypeRef; -use log::debug; +use log::{debug, trace}; use boring::{ error::ErrorStack, @@ -19,9 +19,11 @@ use puffin::{ stream::{MemoryStream, Stream}, trace::TraceContext, }; +use serde::Serialize; use smallvec::SmallVec; use boringssl_sys::ssl_st; +use core::ffi::c_void; use crate::{ boringssl::util::{set_max_protocol_version, static_rsa_cert}, @@ -137,106 +139,6 @@ impl Stream for BoringSSL { } } -fn _to_claim_data( - protocol_version: TLSVersion, - claim: security_claims::Claim, -) -> Option { - match claim.typ { - // Transcripts - security_claims::ClaimType::CLAIM_TRANSCRIPT_CH => Some(ClaimData::Transcript( - ClaimDataTranscript::ClientHello(TranscriptClientHello(TlsTranscript( - claim.transcript.data, - claim.transcript.length, - ))), - )), - security_claims::ClaimType::CLAIM_TRANSCRIPT_PARTIAL_CH => Some(ClaimData::Transcript( - ClaimDataTranscript::PartialClientHello(TranscriptPartialClientHello(TlsTranscript( - claim.transcript.data, - claim.transcript.length, - ))), - )), - security_claims::ClaimType::CLAIM_TRANSCRIPT_CH_SH => Some(ClaimData::Transcript( - ClaimDataTranscript::ServerHello(TranscriptServerHello(TlsTranscript( - claim.transcript.data, - claim.transcript.length, - ))), - )), - security_claims::ClaimType::CLAIM_TRANSCRIPT_CH_SERVER_FIN => Some(ClaimData::Transcript( - ClaimDataTranscript::ServerFinished(TranscriptServerFinished(TlsTranscript( - claim.transcript.data, - claim.transcript.length, - ))), - )), - security_claims::ClaimType::CLAIM_TRANSCRIPT_CH_CLIENT_FIN => Some(ClaimData::Transcript( - ClaimDataTranscript::ClientFinished(TranscriptClientFinished(TlsTranscript( - claim.transcript.data, - claim.transcript.length, - ))), - )), - security_claims::ClaimType::CLAIM_TRANSCRIPT_CH_CERT => Some(ClaimData::Transcript( - ClaimDataTranscript::Certificate(TranscriptCertificate(TlsTranscript( - claim.transcript.data, - claim.transcript.length, - ))), - )), - // Messages - // Transcripts in these messages are not up-to-date. They get updated after the Message has - // been processed - security_claims::ClaimType::CLAIM_FINISHED => { - Some(ClaimData::Message(ClaimDataMessage::Finished(Finished { - outbound: claim.write > 0, - client_random: SmallVec::from(claim.client_random.data), - server_random: SmallVec::from(claim.server_random.data), - session_id: SmallVec::from_slice( - &claim.session_id.data[..claim.session_id.length as usize], - ), - authenticate_peer: false, // FIXME - peer_certificate: Default::default(), // FIXME - master_secret: match protocol_version { - TLSVersion::V1_3 => SmallVec::from_slice(&claim.master_secret.secret), - TLSVersion::V1_2 => SmallVec::from_slice(&claim.master_secret_12.secret), - }, - chosen_cipher: claim.chosen_cipher.data, - available_ciphers: SmallVec::from_iter( - claim.available_ciphers.ciphers[..claim.available_ciphers.length as usize] - .iter() - .map(|cipher| cipher.data), - ), - signature_algorithm: claim.signature_algorithm, - peer_signature_algorithm: claim.peer_signature_algorithm, - }))) - } - security_claims::ClaimType::CLAIM_CLIENT_HELLO => None, - security_claims::ClaimType::CLAIM_CCS => None, - security_claims::ClaimType::CLAIM_END_OF_EARLY_DATA => None, - security_claims::ClaimType::CLAIM_CERTIFICATE => None, - security_claims::ClaimType::CLAIM_KEY_EXCHANGE => None, - // FIXME it is weird that this returns the correct transcript - security_claims::ClaimType::CLAIM_CERTIFICATE_VERIFY => { - if claim.write == 0 { - Some(ClaimData::Transcript(ClaimDataTranscript::ServerFinished( - TranscriptServerFinished(TlsTranscript( - claim.transcript.data, - claim.transcript.length, - )), - ))) - } else { - None - } - } - security_claims::ClaimType::CLAIM_KEY_UPDATE => None, - security_claims::ClaimType::CLAIM_HELLO_REQUEST => None, - security_claims::ClaimType::CLAIM_SERVER_HELLO => None, - security_claims::ClaimType::CLAIM_CERTIFICATE_REQUEST => None, - security_claims::ClaimType::CLAIM_SERVER_DONE => None, - security_claims::ClaimType::CLAIM_SESSION_TICKET => None, - security_claims::ClaimType::CLAIM_CERTIFICATE_STATUS => None, - security_claims::ClaimType::CLAIM_EARLY_DATA => None, - security_claims::ClaimType::CLAIM_ENCRYPTED_EXTENSIONS => None, - _ => None, - } -} - impl Put for BoringSSL { fn progress(&mut self, agent_name: &AgentName) -> Result<(), Error> { let result = if self.is_state_successful() { @@ -263,39 +165,12 @@ impl Put for BoringSSL { #[cfg(feature = "claims")] fn register_claimer(&mut self, agent_name: AgentName) { - self.set_info_callback(Self::create_info_callback(agent_name.clone(), &self.config)) - .expect("Failed to set info_callback to extract transcript"); - - // unsafe { - // use foreign_types_openssl::ForeignTypeRef; - - // let claims = self.config.claims.clone(); - // let protocol_version = self.config.descriptor.tls_version; - // let origin = self.config.descriptor.typ; - - // security_claims::register_claimer( - // self.stream.ssl().as_ptr().cast(), - // move |claim: security_claims::Claim| { - // if let Some(data) = to_claim_data(protocol_version, claim) { - // claims.deref_borrow_mut().claim_sized(TlsClaim { - // agent_name, - // origin, - // protocol_version, - // data, - // }) - // } - // }, - // ); - // } + self.set_msg_callback(Self::create_msg_callback(agent_name.clone(), &self.config)) + .expect("Failed to set msg_callback to extract transcript"); } #[cfg(feature = "claims")] - fn deregister_claimer(&mut self) { - // unsafe { - // use foreign_types_openssl::ForeignTypeRef; - // security_claims::deregister_claimer(self.stream.ssl().as_ptr().cast()); - // } - } + fn deregister_claimer(&mut self) {} #[allow(unused_variables)] fn rename_agent(&mut self, agent_name: AgentName) -> Result<(), Error> { @@ -428,12 +303,12 @@ impl BoringSSL { Ok(ssl) } - /// Set the info_callback of BoringSSL + /// Set the msg_callback of BoringSSL /// - /// Here we use a intermediate callback, `boring_info_callback`, to call the + /// Here we use a intermediate callback, `boring_msg_callback`, to call the /// `callback` function `callback` is stored in the boringssl ex_data to be - /// retrieved and executed by `boring_info_callback` - fn set_info_callback(&mut self, callback: F) -> Result<(), ErrorStack> + /// retrieved and executed by `boring_msg_callback` + fn set_msg_callback(&mut self, callback: F) -> Result<(), ErrorStack> where F: Fn(&mut SslRef, i32) + 'static, { @@ -441,7 +316,7 @@ impl BoringSSL { let ssl = self.stream.ssl_mut(); ssl.set_ex_data(Index::from_raw(0), callback); let ssl_ptr = ssl.as_ptr(); - boringssl_sys::SSL_set_info_callback(ssl_ptr, Some(boring_info_callback::)); + boringssl_sys::SSL_set_msg_callback(ssl_ptr, Some(boring_msg_callback::)); Ok(()) } @@ -449,7 +324,7 @@ impl BoringSSL { /// This callback gets the actual hash transcript of the SSL handshake and /// add it to the claims - fn create_info_callback( + fn create_msg_callback( agent_name: AgentName, config: &TlsPutConfig, ) -> impl Fn(&mut SslRef, i32) { @@ -457,20 +332,38 @@ impl BoringSSL { let protocol_version = config.descriptor.tls_version; let claims = config.claims.clone(); - move |ssl: &mut SslRef, _info_type: i32| { - let claim = match ssl.state_string_long() { - "TLS 1.3 server read_client_finished" => { - extract_current_transcript(ssl).map_or(None, |t| { + move |ssl: &mut SslRef, info_type: i32| { + trace!( + "BORING MSG CALLBACK : {} -- {}", + ssl.state_string_long(), + info_type + ); + + let claim = match (ssl.state_string_long(), info_type) { + ("TLS 1.3 server read_client_finished", 256) => extract_current_transcript(ssl) + .map_or(None, |t| { Some(ClaimData::Transcript(ClaimDataTranscript::ClientFinished( TranscriptClientFinished(t), ))) - }) - } - "TLS 1.3 server done" => extract_current_transcript(ssl).map_or(None, |t| { - Some(ClaimData::Transcript(ClaimDataTranscript::ServerFinished( - TranscriptServerFinished(t), - ))) - }), + }), + ("TLS 1.3 server send_server_hello", 256) => extract_current_transcript(ssl) + .map_or(None, |t| { + Some(ClaimData::Transcript(ClaimDataTranscript::ServerHello( + TranscriptServerHello(t), + ))) + }), + ("TLS 1.3 server send_server_finished", 256) => extract_current_transcript(ssl) + .map_or(None, |t| { + Some(ClaimData::Transcript(ClaimDataTranscript::ServerFinished( + TranscriptServerFinished(t), + ))) + }), + ("TLS 1.3 server read_client_certificate", 256) => extract_current_transcript(ssl) + .map_or(None, |t| { + Some(ClaimData::Transcript(ClaimDataTranscript::Certificate( + TranscriptCertificate(t), + ))) + }), _ => None, }; @@ -494,19 +387,20 @@ pub enum MaybeError { /// This callback uses boringssl ex_data to get a pointer to the real callback /// and to execute it. /// -/// This allows to use a closure as a callback since `SSL_set_info_callback` +/// This allows to use a closure as a callback since `SSL_set_msg_callback` /// accepts only `unsafe extern "C"` functions -pub unsafe extern "C" fn boring_info_callback(ssl: *const ssl_st, info_type: i32, _value: i32) -where +pub unsafe extern "C" fn boring_msg_callback( + _write_p: i32, + _version: i32, + content_type: i32, + _buf: *const c_void, + _len: usize, + ssl: *mut ssl_st, + _arg: *mut c_void, +) where F: Fn(&mut SslRef, i32) + 'static, { - // Exit the callback if it's called from a state that doesn's increase the knowledge - // Here `SSL_CB_ACCEPT_LOOP` seems to allow to get all the transcript changes - // All the states types for the callback are defined in `include/openssl/ssl.h` in BoringSSL - if !(info_type == boringssl_sys::SSL_CB_ACCEPT_LOOP) { - return; - } - let ssl = SslRef::from_ptr_mut(ssl as *mut ssl_st); + let ssl = SslRef::from_ptr_mut(ssl); // Getting the callback from ex_data index 0 let callback = { @@ -517,7 +411,7 @@ where callback.deref() as *const F }; - (*callback)(ssl, info_type); + (*callback)(ssl, content_type); } impl From> for MaybeError { From 1bc1fd0e946cefcfbf4dad977b87ea38fd93efa4 Mon Sep 17 00:00:00 2001 From: Tom Gouville Date: Sun, 18 Feb 2024 18:43:56 +0100 Subject: [PATCH 27/63] update boringssl seeds --- tlspuffin/src/tls/fn_utils.rs | 23 ++++ tlspuffin/src/tls/mod.rs | 2 + tlspuffin/src/tls/seeds.rs | 220 ++++++++++++++++++++++++++++++---- 3 files changed, 222 insertions(+), 23 deletions(-) diff --git a/tlspuffin/src/tls/fn_utils.rs b/tlspuffin/src/tls/fn_utils.rs index 03602f9ed..cb5335bc4 100644 --- a/tlspuffin/src/tls/fn_utils.rs +++ b/tlspuffin/src/tls/fn_utils.rs @@ -6,6 +6,7 @@ use std::convert::TryFrom; use puffin::{ algebra::error::FnError, codec::{Codec, Reader}, + variable_data::VariableData, }; use crate::tls::{ @@ -133,6 +134,28 @@ pub fn fn_find_server_certificate(messages: &Vec) -> Result) -> Result { + for msg in messages { + if let MessagePayload::Handshake(x) = &msg.payload { + if x.typ == HandshakeType::NewSessionTicket { + return Ok(msg.clone()); + } + } + } + Err(FnError::Unknown("no server tickets".to_owned())) +} + +pub fn fn_find_server_certificate_request(messages: &Vec) -> Result { + for msg in messages { + if let MessagePayload::Handshake(x) = &msg.payload { + if x.typ == HandshakeType::CertificateRequest { + return Ok(msg.clone()); + } + } + } + Err(FnError::Unknown("no server tickets".to_owned())) +} + pub fn fn_find_encrypted_extensions(messages: &Vec) -> Result { for msg in messages { if let MessagePayload::Handshake(x) = &msg.payload { diff --git a/tlspuffin/src/tls/mod.rs b/tlspuffin/src/tls/mod.rs index 0d685d868..6530b2c9a 100644 --- a/tlspuffin/src/tls/mod.rs +++ b/tlspuffin/src/tls/mod.rs @@ -208,6 +208,8 @@ define_signature!( fn_decrypt_handshake fn_decrypt_multiple_handshake_messages fn_find_server_certificate + fn_find_server_certificate_request + fn_find_server_ticket fn_find_server_certificate_verify fn_find_encrypted_extensions fn_find_server_finished diff --git a/tlspuffin/src/tls/seeds.rs b/tlspuffin/src/tls/seeds.rs index 983592fe7..a3711f6b3 100644 --- a/tlspuffin/src/tls/seeds.rs +++ b/tlspuffin/src/tls/seeds.rs @@ -937,6 +937,156 @@ pub fn seed_client_attacker_auth(server: AgentName) -> Trace { } } +// BUG: `BAD_SIGNATURE` error +pub fn seed_client_attacker_auth_boring(server: AgentName) -> Trace { + let client_hello = term! { + fn_client_hello( + fn_protocol_version12, + fn_new_random, + fn_new_session_id, + (fn_append_cipher_suite( + (fn_new_cipher_suites()), + fn_cipher_suite13_aes_128_gcm_sha256 + )), + fn_compressions, + (fn_client_extensions_append( + (fn_client_extensions_append( + (fn_client_extensions_append( + (fn_client_extensions_append( + fn_client_extensions_new, + (fn_support_group_extension(fn_named_group_secp384r1)) + )), + fn_signature_algorithm_extension + )), + (fn_key_share_deterministic_extension(fn_named_group_secp384r1)) + )), + fn_supported_versions13_extension + )) + ) + }; + + let extensions = term! { + fn_decrypt_multiple_handshake_messages( + ((server, 0)[Some(TlsQueryMatcher::ApplicationData)]), // Encrypted Extensions + (fn_server_hello_transcript(((server, 0)))), + (fn_get_server_key_share(((server, 0)[Some(TlsQueryMatcher::Handshake(Some(HandshakeType::ServerHello)))]))), + fn_no_psk, + fn_named_group_secp384r1, + fn_true, + fn_seq_0 // sequence 0 + ) + }; + + // ApplicationData 0 is EncryptedExtensions + let certificate_request_message = term! { + fn_find_server_certificate_request((@extensions)) + }; + + let certificate = term! { + fn_certificate13( + (fn_get_context((@certificate_request_message))), + (fn_append_certificate_entry( + (fn_certificate_entry( + fn_bob_cert + )), + fn_empty_certificate_chain + )) + ) + }; + + let certificate_verify = term! { + fn_certificate_verify( + fn_rsa_pss_signature_algorithm, + (fn_rsa_sign_client( + (fn_certificate_transcript(((server, 0)))), + fn_bob_key, + fn_rsa_pss_signature_algorithm + )) + ) + }; + + let client_finished = term! { + fn_finished( + (fn_verify_data( + (fn_server_finished_transcript(((server, 0)))), + (fn_server_hello_transcript(((server, 0)))), + (fn_get_server_key_share(((server, 0)))), + fn_no_psk, + fn_named_group_secp384r1 + )) + ) + }; + + Trace { + prior_traces: vec![], + descriptors: vec![AgentDescriptor { + name: server, + tls_version: TLSVersion::V1_3, + typ: AgentType::Server, + client_authentication: true, + ..AgentDescriptor::default() + }], + steps: vec![ + Step { + agent: server, + action: Action::Input(InputAction { + recipe: term! { + @client_hello + }, + }), + }, + Step { + agent: server, + action: Action::Input(InputAction { + recipe: term! { + fn_encrypt_handshake( + (@certificate), + (fn_server_hello_transcript(((server, 0)))), + (fn_get_server_key_share(((server, 0)))), + fn_no_psk, + fn_named_group_secp384r1, + fn_true, + fn_seq_0 // sequence 0 + ) + }, + }), + }, + Step { + agent: server, + action: Action::Input(InputAction { + recipe: term! { + fn_encrypt_handshake( + (@certificate_verify), + (fn_server_hello_transcript(((server, 0)))), + (fn_get_server_key_share(((server, 0)))), + fn_no_psk, + fn_named_group_secp384r1, + fn_true, + fn_seq_1 // sequence 1 + ) + }, + }), + }, + Step { + agent: server, + action: Action::Input(InputAction { + recipe: term! { + fn_encrypt_handshake( + (@client_finished), + (fn_server_hello_transcript(((server, 0)))), + (fn_get_server_key_share(((server, 0)))), + fn_no_psk, + fn_named_group_secp384r1, + fn_true, + fn_seq_2 // sequence 2 + ) + }, + }), + }, + ], + } +} + pub fn seed_client_attacker(server: AgentName) -> Trace { let client_hello = term! { fn_client_hello( @@ -1605,12 +1755,12 @@ pub fn _seed_client_attacker_full( ) } -pub fn seed_client_attacker_boring(server: AgentName) -> Trace { - _seed_client_attacker_boring(server).0 +pub fn seed_client_attacker_full_boring(server: AgentName) -> Trace { + _seed_client_attacker_full_boring(server).0 } /// Seed which contains the whole transcript in the tree. This is rather huge >300 symbols -pub fn _seed_client_attacker_boring( +pub fn _seed_client_attacker_full_boring( server: AgentName, ) -> ( Trace, @@ -1999,25 +2149,45 @@ macro_rules! corpus { } pub fn create_corpus() -> Vec<(Trace, &'static str)> { - corpus!( - // Full Handshakes - seed_successful: cfg(feature = "tls13"), - seed_successful_with_ccs: cfg(feature = "tls13"), - seed_successful_with_tickets: cfg(feature = "tls13"), - seed_successful12: cfg(not(feature = "tls12-session-resumption")), - seed_successful12_with_tickets: cfg(feature = "tls12-session-resumption"), - // Client Attackers - seed_client_attacker: cfg(feature = "tls13"), - seed_client_attacker_full: cfg(feature = "tls13"), - seed_client_attacker_boring: cfg(feature = "tls13"), - seed_client_attacker_auth: cfg(all(feature = "tls13", feature = "client-authentication-transcript-extraction")), - seed_client_attacker12: cfg(feature = "tls12"), - // Session resumption - seed_session_resumption_dhe: cfg(all(feature = "tls13", feature = "tls13-session-resumption")), - seed_session_resumption_ke: cfg(all(feature = "tls13", feature = "tls13-session-resumption")), - // Server Attackers - seed_server_attacker_full: cfg(feature = "tls13") - ) + if cfg!(not(feature = "boringssl-binding")) { + corpus!( + // Full Handshakes + seed_successful: cfg(feature = "tls13"), + seed_successful_with_ccs: cfg(feature = "tls13"), + seed_successful_with_tickets: cfg(feature = "tls13"), + seed_successful12: cfg(not(feature = "tls12-session-resumption")), + seed_successful12_with_tickets: cfg(feature = "tls12-session-resumption"), + // Client Attackers + seed_client_attacker: cfg(feature = "tls13"), + seed_client_attacker_full: cfg(feature = "tls13"), + seed_client_attacker_auth: cfg(all(feature = "tls13", feature = "client-authentication-transcript-extraction")), + seed_client_attacker12: cfg(feature = "tls12"), + // Session resumption + seed_session_resumption_dhe: cfg(all(feature = "tls13", feature = "tls13-session-resumption")), + seed_session_resumption_ke: cfg(all(feature = "tls13", feature = "tls13-session-resumption")), + // Server Attackers + seed_server_attacker_full: cfg(feature = "tls13") + ) + } else { + corpus!( + // Full Handhakes + seed_successful: cfg(feature = "tls13"), + seed_successful_with_ccs: cfg(feature = "tls13"), + seed_successful_with_tickets: cfg(feature = "tls13"), + seed_successful12: cfg(not(feature = "tls12-session-resumption")), + seed_successful12_with_tickets: cfg(feature = "tls12-session-resumption"), + // Client Attackers + seed_client_attacker: cfg(feature = "tls13"), + seed_client_attacker_full_boring: cfg(feature = "tls13"), + seed_client_attacker_auth_boring: cfg(all(feature = "tls13", feature = "client-authentication-transcript-extraction")), + seed_client_attacker12: cfg(feature = "tls12"), + // Session resumption + seed_session_resumption_dhe: cfg(all(feature = "tls13", feature = "tls13-session-resumption")), + seed_session_resumption_ke: cfg(all(feature = "tls13", feature = "tls13-session-resumption")), + // Server Attackers + seed_server_attacker_full: cfg(feature = "tls13") + ) + } } #[cfg(test)] @@ -2068,7 +2238,11 @@ pub mod tests { fn test_seed_client_attacker_full() { use crate::tls::trace_helper::TraceExecutor; - let ctx = seed_client_attacker_full.execute_trace(); + let ctx = if cfg!(not(feature = "boringssl-binding")) { + seed_client_attacker_full.execute_trace() + } else { + seed_client_attacker_full_boring.execute_trace() + }; assert!(ctx.agents_successful()); } From df0851e9ec75782f0df7e5e552735657cab96ca4 Mon Sep 17 00:00:00 2001 From: Tom Gouville Date: Fri, 8 Mar 2024 11:14:55 +0100 Subject: [PATCH 28/63] fixing warnings --- tlspuffin/src/boringssl/mod.rs | 51 ++++++++++++--------------- tlspuffin/src/boringssl/transcript.rs | 1 - 2 files changed, 22 insertions(+), 30 deletions(-) diff --git a/tlspuffin/src/boringssl/mod.rs b/tlspuffin/src/boringssl/mod.rs index d7d77d93a..344c95825 100644 --- a/tlspuffin/src/boringssl/mod.rs +++ b/tlspuffin/src/boringssl/mod.rs @@ -1,17 +1,31 @@ -use std::{borrow::Borrow, cell::RefCell, fmt::Pointer, io::ErrorKind, rc::Rc}; - -use foreign_types::ForeignTypeRef; -use log::{debug, trace}; - +use crate::{ + boringssl::util::{set_max_protocol_version, static_rsa_cert}, + claims::{ + ClaimData, ClaimDataTranscript, TlsClaim, TranscriptCertificate, TranscriptClientFinished, + TranscriptServerFinished, TranscriptServerHello, + }, + protocol::TLSProtocolBehavior, + put::TlsPutConfig, + put_registry::BORINGSSL_PUT, + static_certs::{ALICE_CERT, ALICE_PRIVATE_KEY, BOB_CERT, BOB_PRIVATE_KEY, EVE_CERT}, + tls::rustls::msgs::{ + deframer::MessageDeframer, + message::{Message, OpaqueMessage}, + }, +}; use boring::{ error::ErrorStack, ex_data::Index, ssl::{Ssl, SslContext, SslMethod, SslRef, SslStream, SslVerifyMode}, x509::{store::X509StoreBuilder, X509}, }; +use boringssl_sys::ssl_st; +use core::ffi::c_void; +use foreign_types::ForeignTypeRef; use log::info; +use log::{debug, trace}; use puffin::{ - agent::{AgentDescriptor, AgentName, AgentType, TLSVersion}, + agent::{AgentDescriptor, AgentName, AgentType}, error::Error, protocol::MessageResult, put::{Put, PutName}, @@ -19,28 +33,7 @@ use puffin::{ stream::{MemoryStream, Stream}, trace::TraceContext, }; -use serde::Serialize; -use smallvec::SmallVec; - -use boringssl_sys::ssl_st; -use core::ffi::c_void; - -use crate::{ - boringssl::util::{set_max_protocol_version, static_rsa_cert}, - claims::{ - ClaimData, ClaimDataMessage, ClaimDataTranscript, Finished, TlsClaim, TlsTranscript, - TranscriptCertificate, TranscriptClientFinished, TranscriptClientHello, - TranscriptPartialClientHello, TranscriptServerFinished, TranscriptServerHello, - }, - protocol::TLSProtocolBehavior, - put::TlsPutConfig, - put_registry::BORINGSSL_PUT, - static_certs::{ALICE_CERT, ALICE_PRIVATE_KEY, BOB_CERT, BOB_PRIVATE_KEY, EVE_CERT}, - tls::rustls::msgs::{ - deframer::MessageDeframer, - message::{Message, OpaqueMessage}, - }, -}; +use std::{cell::RefCell, io::ErrorKind, rc::Rc}; #[cfg(feature = "deterministic")] mod deterministic; @@ -140,7 +133,7 @@ impl Stream for BoringSSL { } impl Put for BoringSSL { - fn progress(&mut self, agent_name: &AgentName) -> Result<(), Error> { + fn progress(&mut self, _agent_name: &AgentName) -> Result<(), Error> { let result = if self.is_state_successful() { // Trigger another read let mut vec: Vec = Vec::from([1; 128]); diff --git a/tlspuffin/src/boringssl/transcript.rs b/tlspuffin/src/boringssl/transcript.rs index afe46127b..f8aac48f0 100644 --- a/tlspuffin/src/boringssl/transcript.rs +++ b/tlspuffin/src/boringssl/transcript.rs @@ -1,6 +1,5 @@ use boring::ssl::SslRef; use boringssl_sys as boringssl; -use core::ffi::{c_char, CStr}; use foreign_types::ForeignTypeRef; use crate::claims::TlsTranscript; From 5e75deffa7affc75dd1a8b011c7486446e2f85d7 Mon Sep 17 00:00:00 2001 From: Tom Gouville Date: Fri, 8 Mar 2024 11:37:07 +0100 Subject: [PATCH 29/63] replacing feature name boringsslv1 with boringssl202311 --- crates/boringssl-sys/Cargo.toml | 2 +- crates/boringssl-sys/build.rs | 2 +- tlspuffin/Cargo.toml | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/crates/boringssl-sys/Cargo.toml b/crates/boringssl-sys/Cargo.toml index 3ab0e1da9..30c0b9dfb 100644 --- a/crates/boringssl-sys/Cargo.toml +++ b/crates/boringssl-sys/Cargo.toml @@ -10,7 +10,7 @@ bindgen = "0.69.1" [features] vendored-master = [] -arbitrary-v1 = [] +boring-2023-11-18 = [] sancov = [] asan = [] diff --git a/crates/boringssl-sys/build.rs b/crates/boringssl-sys/build.rs index 851d47756..ef966eb5b 100644 --- a/crates/boringssl-sys/build.rs +++ b/crates/boringssl-sys/build.rs @@ -3,7 +3,7 @@ use std::{env, path::PathBuf}; use boringssl_src::{build, BoringSSLOptions, GitRef}; fn main() { - let git_ref: GitRef = if cfg!(feature = "arbitrary-v1") { + let git_ref: GitRef = if cfg!(feature = "boring-2023-11-18") { GitRef::Commit(String::from("698aa894c96412d4df20e2bb031d9eb9c9d5919a")) } else if cfg!(feature = "vendored-master") { GitRef::Branch(String::from("master")) diff --git a/tlspuffin/Cargo.toml b/tlspuffin/Cargo.toml index 1e5caa7d2..8803dfbbf 100644 --- a/tlspuffin/Cargo.toml +++ b/tlspuffin/Cargo.toml @@ -200,8 +200,8 @@ boringsslmaster = [ "claims" ] -boringsslv1 = [ - "boringssl-sys/arbitrary-v1", +boringssl202311 = [ + "boringssl-sys/boring-2023-11-18", "tls12", "tls13", "boringssl-binding", From 73492e4039cad4891816fb6391b69d492f8c7f43 Mon Sep 17 00:00:00 2001 From: Tom Gouville Date: Wed, 13 Mar 2024 15:56:02 +0100 Subject: [PATCH 30/63] fix Boringssl master build --- crates/boringssl-sys/build.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/crates/boringssl-sys/build.rs b/crates/boringssl-sys/build.rs index ef966eb5b..4e9efa8d2 100644 --- a/crates/boringssl-sys/build.rs +++ b/crates/boringssl-sys/build.rs @@ -32,6 +32,7 @@ fn main() { println!("cargo:rustc-link-search={}/lib", out_dir.display()); println!("cargo:rustc-link-lib=static=crypto"); println!("cargo:rustc-link-lib=static=ssl"); + println!("cargo:rustc-link-lib=stdc++"); let include_path = out_dir.join("include"); From b4f9efd93eda2a7bde3aee1ad40ce649b1d3a7d9 Mon Sep 17 00:00:00 2001 From: Tom Gouville Date: Wed, 13 Mar 2024 16:35:34 +0100 Subject: [PATCH 31/63] disabling some boringssl unit tests (seeds which are not currently working and race condition on determinism) --- tlspuffin/src/boringssl/deterministic.rs | 2 ++ tlspuffin/src/tls/seeds.rs | 10 ++++++++++ 2 files changed, 12 insertions(+) diff --git a/tlspuffin/src/boringssl/deterministic.rs b/tlspuffin/src/boringssl/deterministic.rs index 24cd10627..bf2df790a 100644 --- a/tlspuffin/src/boringssl/deterministic.rs +++ b/tlspuffin/src/boringssl/deterministic.rs @@ -17,6 +17,8 @@ mod tests { use puffin::trace::{Action, InputAction, OutputAction, Step, Trace, TraceContext}; use std::fmt::format; + // BUG: This test only works in a single threaded cargo test execution + #[ignore] #[test] fn test_boringssl_no_randomness_full() { let trace = seed_client_attacker_full_boring.build_trace(); diff --git a/tlspuffin/src/tls/seeds.rs b/tlspuffin/src/tls/seeds.rs index a3711f6b3..81ea50e13 100644 --- a/tlspuffin/src/tls/seeds.rs +++ b/tlspuffin/src/tls/seeds.rs @@ -2225,6 +2225,7 @@ pub mod tests { #[cfg(feature = "tls13")] // require version which supports TLS 1.3 #[cfg(feature = "client-authentication-transcript-extraction")] + #[cfg(not(feature = "boringssl-binding"))] #[test] fn test_seed_client_attacker_auth() { use crate::tls::trace_helper::TraceExecutor; @@ -2247,6 +2248,7 @@ pub mod tests { } #[cfg(feature = "tls13")] // require version which supports TLS 1.3 + #[cfg(not(feature = "boringssl-binding"))] #[test] fn test_seed_server_attacker_full() { use crate::tls::trace_helper::TraceExecutor; @@ -2257,6 +2259,7 @@ pub mod tests { #[cfg(all(feature = "tls13", feature = "tls13-session-resumption"))] #[cfg(not(feature = "wolfssl-disable-postauth"))] + #[cfg(not(feature = "boringssl-binding"))] #[test] fn test_seed_session_resumption_dhe() { use crate::tls::trace_helper::TraceExecutor; @@ -2267,6 +2270,7 @@ pub mod tests { #[cfg(all(feature = "tls13", feature = "tls13-session-resumption"))] #[cfg(not(feature = "wolfssl-disable-postauth"))] + #[cfg(not(feature = "boringssl-binding"))] #[test] fn test_seed_session_resumption_dhe_full() { use crate::tls::trace_helper::TraceExecutor; @@ -2277,6 +2281,7 @@ pub mod tests { #[cfg(all(feature = "tls13", feature = "tls13-session-resumption"))] #[cfg(not(feature = "wolfssl-disable-postauth"))] + #[cfg(not(feature = "boringssl-binding"))] #[test] fn test_seed_session_resumption_ke() { use crate::tls::trace_helper::TraceExecutor; @@ -2286,6 +2291,7 @@ pub mod tests { } #[cfg(feature = "tls13")] // require version which supports TLS 1.3 + #[cfg(not(feature = "boringssl-binding"))] #[test] fn test_seed_successful() { use crate::tls::trace_helper::TraceExecutor; @@ -2295,6 +2301,7 @@ pub mod tests { } #[cfg(feature = "tls13")] // require version which supports TLS 1.3 + #[cfg(not(feature = "boringssl-binding"))] #[test] fn test_seed_successful_client_auth() { use crate::tls::trace_helper::TraceExecutor; @@ -2317,6 +2324,7 @@ pub mod tests { } #[cfg(feature = "tls13")] // require version which supports TLS 1.3 + #[cfg(not(feature = "boringssl-binding"))] #[test] fn test_seed_successful_with_ccs() { use crate::tls::trace_helper::TraceExecutor; @@ -2328,6 +2336,7 @@ pub mod tests { // require version which supports TLS 1.3 and session resumption (else no tickets are sent) // LibreSSL does not yet support PSK #[cfg(all(feature = "tls13", feature = "tls13-session-resumption"))] + #[cfg(not(feature = "boringssl-binding"))] #[test] fn test_seed_successful_with_tickets() { use crate::tls::trace_helper::TraceExecutor; @@ -2338,6 +2347,7 @@ pub mod tests { #[test] #[cfg(feature = "tls12")] + #[cfg(not(feature = "boringssl-binding"))] fn test_seed_successful12() { use crate::tls::trace_helper::TraceExecutor; From 2a0619f4ad4324cd264c76ead7e5ca208212d8c1 Mon Sep 17 00:00:00 2001 From: Tom Gouville Date: Wed, 13 Mar 2024 17:22:04 +0100 Subject: [PATCH 32/63] fix ci errors --- puffin/src/put_registry.rs | 6 ++++-- sshpuffin/src/protocol.rs | 3 +-- tlspuffin/src/integration_tests/determinism.rs | 11 ++++++----- tlspuffin/src/openssl/deterministic.rs | 3 ++- tlspuffin/src/openssl/mod.rs | 6 ++++-- tlspuffin/src/wolfssl/mod.rs | 10 +++++++++- 6 files changed, 26 insertions(+), 13 deletions(-) diff --git a/puffin/src/put_registry.rs b/puffin/src/put_registry.rs index 6a62efd4a..30d243fd9 100644 --- a/puffin/src/put_registry.rs +++ b/puffin/src/put_registry.rs @@ -1,3 +1,7 @@ +use std::fmt::{Debug, Formatter}; + +use log::debug; + use crate::{ agent::AgentDescriptor, error::Error, @@ -5,8 +9,6 @@ use crate::{ put::{Put, PutName}, trace::TraceContext, }; -use log::debug; -use std::fmt::{Debug, Formatter}; pub const DUMMY_PUT: PutName = PutName(['D', 'U', 'M', 'Y', 'Y', 'D', 'U', 'M', 'M', 'Y']); diff --git a/sshpuffin/src/protocol.rs b/sshpuffin/src/protocol.rs index f5fabe8ed..fbe76466d 100644 --- a/sshpuffin/src/protocol.rs +++ b/sshpuffin/src/protocol.rs @@ -25,10 +25,9 @@ use crate::{ SSH_PUT_REGISTRY, }; -#[derive(Clone)] +#[derive(Clone, Debug, PartialEq)] pub struct SshProtocolBehavior {} -#[derive(Debug, PartialEq)] impl ProtocolBehavior for SshProtocolBehavior { type Claim = SshClaim; type SecurityViolationPolicy = SshSecurityViolationPolicy; diff --git a/tlspuffin/src/integration_tests/determinism.rs b/tlspuffin/src/integration_tests/determinism.rs index 2ad7a577d..300bb0c73 100644 --- a/tlspuffin/src/integration_tests/determinism.rs +++ b/tlspuffin/src/integration_tests/determinism.rs @@ -1,8 +1,9 @@ -use crate::put_registry::TLS_PUT_REGISTRY; -use crate::tls::seeds::seed_client_attacker_full; -use crate::tls::trace_helper::TraceHelper; -use puffin::put::PutOptions; -use puffin::trace::TraceContext; +use puffin::{put::PutOptions, trace::TraceContext}; + +use crate::{ + put_registry::TLS_PUT_REGISTRY, + tls::{seeds::seed_client_attacker_full, trace_helper::TraceHelper}, +}; #[test] #[cfg(all(feature = "deterministic", feature = "tls13"))] diff --git a/tlspuffin/src/openssl/deterministic.rs b/tlspuffin/src/openssl/deterministic.rs index 08cfcfcaa..73d91d337 100644 --- a/tlspuffin/src/openssl/deterministic.rs +++ b/tlspuffin/src/openssl/deterministic.rs @@ -34,9 +34,10 @@ pub fn determinism_reseed_openssl() { #[cfg(test)] mod tests { - use crate::openssl::deterministic::{determinism_set_reseed_openssl, get_seed}; use openssl::rand::rand_bytes; + use crate::openssl::deterministic::{determinism_set_reseed_openssl, get_seed}; + #[test] #[cfg(feature = "openssl111-binding")] fn test_openssl_no_randomness_simple() { diff --git a/tlspuffin/src/openssl/mod.rs b/tlspuffin/src/openssl/mod.rs index 7f5547a25..b2aae22b3 100644 --- a/tlspuffin/src/openssl/mod.rs +++ b/tlspuffin/src/openssl/mod.rs @@ -29,14 +29,16 @@ use puffin::{ }; use smallvec::SmallVec; -use crate::openssl::deterministic::{determinism_reseed_openssl, determinism_set_reseed_openssl}; use crate::{ claims::{ ClaimData, ClaimDataMessage, ClaimDataTranscript, ClientHello, Finished, TlsClaim, TlsTranscript, TranscriptCertificate, TranscriptClientFinished, TranscriptClientHello, TranscriptPartialClientHello, TranscriptServerFinished, TranscriptServerHello, }, - openssl::util::{set_max_protocol_version, static_rsa_cert}, + openssl::{ + deterministic::{determinism_reseed_openssl, determinism_set_reseed_openssl}, + util::{set_max_protocol_version, static_rsa_cert}, + }, protocol::TLSProtocolBehavior, put::TlsPutConfig, put_registry::OPENSSL111_PUT, diff --git a/tlspuffin/src/wolfssl/mod.rs b/tlspuffin/src/wolfssl/mod.rs index 61379f0c8..67366f82d 100644 --- a/tlspuffin/src/wolfssl/mod.rs +++ b/tlspuffin/src/wolfssl/mod.rs @@ -12,7 +12,7 @@ use std::{ }; use foreign_types::{ForeignType, ForeignTypeRef}; -use log::warn; +use log::{error, warn}; use puffin::{ agent::{AgentDescriptor, AgentName, AgentType, TLSVersion}, algebra::dynamic_function::TypeShape, @@ -92,6 +92,14 @@ pub fn new_wolfssl_factory() -> Box> { fn version(&self) -> String { WolfSSL::version() } + + fn determinism_set_reseed(&self) -> () { + error!("[determinism_set_reseed] Not yet implemented.") + } + + fn determinism_reseed(&self) -> () { + error!("[determinism_reseed] Not yet implemented.") + } } Box::new(WolfSSLFactory) From d747acf167cda56fa5ca47d770cced6c406bb26e Mon Sep 17 00:00:00 2001 From: Tom Gouville Date: Wed, 13 Mar 2024 18:05:27 +0100 Subject: [PATCH 33/63] fixing problem with the deterministic feature in openssl --- tlspuffin/src/openssl/mod.rs | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/tlspuffin/src/openssl/mod.rs b/tlspuffin/src/openssl/mod.rs index b352882ee..3a7a6226d 100644 --- a/tlspuffin/src/openssl/mod.rs +++ b/tlspuffin/src/openssl/mod.rs @@ -22,10 +22,7 @@ use crate::{ TlsTranscript, TranscriptCertificate, TranscriptClientFinished, TranscriptClientHello, TranscriptPartialClientHello, TranscriptServerFinished, TranscriptServerHello, }, - openssl::{ - deterministic::{determinism_reseed_openssl, determinism_set_reseed_openssl}, - util::{set_max_protocol_version, static_rsa_cert}, - }, + openssl::util::{set_max_protocol_version, static_rsa_cert}, protocol::TLSProtocolBehavior, put::TlsPutConfig, put_registry::OPENSSL111_PUT, @@ -95,12 +92,14 @@ pub fn new_openssl_factory() -> Box> { fn determinism_set_reseed(&self) -> () { debug!("[Determinism] set and reseed"); - determinism_set_reseed_openssl(); + #[cfg(feature = "deterministic")] + deterministic::determinism_set_reseed_openssl(); } fn determinism_reseed(&self) -> () { debug!("[Determinism] reseed"); - determinism_reseed_openssl(); + #[cfg(feature = "deterministic")] + deterministic::determinism_reseed_openssl(); } } From aeb988d8e110c6a757107d963e56c7338fa8f4b2 Mon Sep 17 00:00:00 2001 From: Tom Gouville Date: Wed, 13 Mar 2024 18:07:41 +0100 Subject: [PATCH 34/63] fmt --- crates/boring/examples/mk_certs.rs | 24 ++--- crates/boring/src/aes.rs | 7 +- crates/boring/src/asn1.rs | 32 +++---- crates/boring/src/base64.rs | 5 +- crates/boring/src/bio.rs | 9 +- crates/boring/src/bn.rs | 24 ++--- crates/boring/src/conf.rs | 4 +- crates/boring/src/derive.rs | 23 ++--- crates/boring/src/dh.rs | 24 ++--- crates/boring/src/dsa.rs | 19 ++-- crates/boring/src/ec.rs | 24 ++--- crates/boring/src/ecdsa.rs | 18 ++-- crates/boring/src/error.rs | 9 +- crates/boring/src/ex_data.rs | 3 +- crates/boring/src/hash.rs | 30 ++++--- crates/boring/src/lib.rs | 5 +- crates/boring/src/memcmp.rs | 3 +- crates/boring/src/nid.rs | 9 +- crates/boring/src/pkcs12.rs | 36 ++++---- crates/boring/src/pkcs5.rs | 12 +-- crates/boring/src/pkey.rs | 34 ++++--- crates/boring/src/rand.rs | 4 +- crates/boring/src/rsa.rs | 20 ++--- crates/boring/src/sha.rs | 6 +- crates/boring/src/sign.rs | 42 +++++---- crates/boring/src/srtp.rs | 8 +- crates/boring/src/ssl/bio.rs | 28 +++--- crates/boring/src/ssl/callbacks.rs | 22 +++-- crates/boring/src/ssl/connector.rs | 23 ++--- crates/boring/src/ssl/error.rs | 10 +-- crates/boring/src/ssl/mod.rs | 89 ++++++++++--------- crates/boring/src/ssl/test/mod.rs | 44 ++++----- .../boring/src/ssl/test/private_key_method.rs | 33 ++++--- crates/boring/src/ssl/test/server.rs | 8 +- crates/boring/src/ssl/test/session.rs | 15 ++-- crates/boring/src/stack.rs | 35 ++++---- crates/boring/src/string.rs | 10 +-- crates/boring/src/symm.rs | 12 ++- crates/boring/src/util.rs | 9 +- crates/boring/src/x509/extension.rs | 11 ++- crates/boring/src/x509/mod.rs | 66 +++++++------- crates/boring/src/x509/store.rs | 15 ++-- crates/boring/src/x509/tests.rs | 28 +++--- crates/boring/src/x509/verify.rs | 7 +- crates/boringssl-sys/src/lib.rs | 9 +- sshpuffin/src/libssh/mod.rs | 1 - tlspuffin/src/boringssl/deterministic.rs | 20 +++-- tlspuffin/src/boringssl/mod.rs | 40 +++++---- tlspuffin/src/tls/seeds.rs | 3 +- 49 files changed, 508 insertions(+), 464 deletions(-) diff --git a/crates/boring/examples/mk_certs.rs b/crates/boring/examples/mk_certs.rs index ce0c8492d..bc5fdd009 100644 --- a/crates/boring/examples/mk_certs.rs +++ b/crates/boring/examples/mk_certs.rs @@ -3,17 +3,21 @@ extern crate boring; -use boring::asn1::Asn1Time; -use boring::bn::{BigNum, MsbOption}; -use boring::error::ErrorStack; -use boring::hash::MessageDigest; -use boring::pkey::{PKey, PKeyRef, Private}; -use boring::rsa::Rsa; -use boring::x509::extension::{ - AuthorityKeyIdentifier, BasicConstraints, KeyUsage, SubjectAlternativeName, - SubjectKeyIdentifier, +use boring::{ + asn1::Asn1Time, + bn::{BigNum, MsbOption}, + error::ErrorStack, + hash::MessageDigest, + pkey::{PKey, PKeyRef, Private}, + rsa::Rsa, + x509::{ + extension::{ + AuthorityKeyIdentifier, BasicConstraints, KeyUsage, SubjectAlternativeName, + SubjectKeyIdentifier, + }, + X509NameBuilder, X509Ref, X509Req, X509ReqBuilder, X509, + }, }; -use boring::x509::{X509NameBuilder, X509Ref, X509Req, X509ReqBuilder, X509}; /// Make a CA certificate and private key fn mk_ca_cert() -> Result<(X509, PKey), ErrorStack> { diff --git a/crates/boring/src/aes.rs b/crates/boring/src/aes.rs index 12288763e..c1540f212 100644 --- a/crates/boring/src/aes.rs +++ b/crates/boring/src/aes.rs @@ -37,10 +37,11 @@ //! assert_eq!(&orig_key[..], &key_to_wrap[..]); //! ``` //! -use crate::ffi; +use std::{mem::MaybeUninit, ptr}; + use libc::{c_int, c_uint, size_t}; -use std::mem::MaybeUninit; -use std::ptr; + +use crate::ffi; /// Provides Error handling for parsing keys. #[derive(Debug)] diff --git a/crates/boring/src/asn1.rs b/crates/boring/src/asn1.rs index 02e601fcf..98f2c9796 100644 --- a/crates/boring/src/asn1.rs +++ b/crates/boring/src/asn1.rs @@ -24,23 +24,21 @@ //! use boring::asn1::Asn1Time; //! let tomorrow = Asn1Time::days_from_now(1); //! ``` -use crate::ffi; +use std::{cmp::Ordering, ffi::CString, fmt, ptr, slice, str}; + use foreign_types::{ForeignType, ForeignTypeRef}; use libc::{c_char, c_int, c_long, time_t}; -use std::cmp::Ordering; -use std::ffi::CString; -use std::fmt; -use std::ptr; -use std::slice; -use std::str; - -use crate::bio::MemBio; -use crate::bn::{BigNum, BigNumRef}; -use crate::error::ErrorStack; -use crate::nid::Nid; -use crate::stack::Stackable; -use crate::string::OpensslString; -use crate::{cvt, cvt_p}; + +use crate::{ + bio::MemBio, + bn::{BigNum, BigNumRef}, + cvt, cvt_p, + error::ErrorStack, + ffi, + nid::Nid, + stack::Stackable, + string::OpensslString, +}; foreign_type_and_impl_send_sync! { type CType = ffi::ASN1_GENERALIZEDTIME; @@ -614,9 +612,7 @@ use crate::ffi::ASN1_STRING_get0_data; #[cfg(test)] mod tests { use super::*; - - use crate::bn::BigNum; - use crate::nid::Nid; + use crate::{bn::BigNum, nid::Nid}; /// Tests conversion between BigNum and Asn1Integer. #[test] diff --git a/crates/boring/src/base64.rs b/crates/boring/src/base64.rs index 2e9ef17d3..182a6c2c6 100644 --- a/crates/boring/src/base64.rs +++ b/crates/boring/src/base64.rs @@ -1,9 +1,8 @@ //! Base64 encoding support. -use crate::cvt_n; -use crate::error::ErrorStack; -use crate::ffi; use libc::c_int; +use crate::{cvt_n, error::ErrorStack, ffi}; + /// Encodes a slice of bytes to a base64 string. /// /// This corresponds to [`EVP_EncodeBlock`]. diff --git a/crates/boring/src/bio.rs b/crates/boring/src/bio.rs index adb119b87..621eadf69 100644 --- a/crates/boring/src/bio.rs +++ b/crates/boring/src/bio.rs @@ -1,11 +1,6 @@ -use crate::ffi; -use crate::ffi::BIO_new_mem_buf; -use std::marker::PhantomData; -use std::ptr; -use std::slice; +use std::{marker::PhantomData, ptr, slice}; -use crate::cvt_p; -use crate::error::ErrorStack; +use crate::{cvt_p, error::ErrorStack, ffi, ffi::BIO_new_mem_buf}; pub struct MemBioSlice<'a>(*mut ffi::BIO, PhantomData<&'a [u8]>); diff --git a/crates/boring/src/bn.rs b/crates/boring/src/bn.rs index 6ad257825..1a3c42265 100644 --- a/crates/boring/src/bn.rs +++ b/crates/boring/src/bn.rs @@ -22,19 +22,21 @@ //! ``` //! //! [`BIGNUM`]: https://wiki.openssl.org/index.php/Manual:Bn_internal(3) -use crate::ffi; +use std::{ + cmp::Ordering, + ffi::CString, + fmt, + ops::{Add, Deref, Div, Mul, Neg, Rem, Shl, Shr, Sub}, + ptr, +}; + use foreign_types::{ForeignType, ForeignTypeRef}; use libc::{c_int, size_t}; -use std::cmp::Ordering; -use std::ffi::CString; -use std::ops::{Add, Deref, Div, Mul, Neg, Rem, Shl, Shr, Sub}; -use std::{fmt, ptr}; - -use crate::asn1::Asn1Integer; -use crate::error::ErrorStack; -use crate::ffi::BN_is_negative; -use crate::string::OpensslString; -use crate::{cvt, cvt_n, cvt_p}; + +use crate::{ + asn1::Asn1Integer, cvt, cvt_n, cvt_p, error::ErrorStack, ffi, ffi::BN_is_negative, + string::OpensslString, +}; /// Options for the most significant bits of a randomly generated `BigNum`. pub struct MsbOption(c_int); diff --git a/crates/boring/src/conf.rs b/crates/boring/src/conf.rs index c94dde4a8..2503e245f 100644 --- a/crates/boring/src/conf.rs +++ b/crates/boring/src/conf.rs @@ -1,10 +1,8 @@ //! Interface for processing OpenSSL configuration files. -use crate::ffi; use foreign_types::ForeignType; use libc::c_void; -use crate::cvt_p; -use crate::error::ErrorStack; +use crate::{cvt_p, error::ErrorStack, ffi}; pub struct ConfMethod(*mut c_void); diff --git a/crates/boring/src/derive.rs b/crates/boring/src/derive.rs index 08a47dfb5..6fe9ab8ac 100644 --- a/crates/boring/src/derive.rs +++ b/crates/boring/src/derive.rs @@ -1,12 +1,14 @@ //! Shared secret derivation. -use crate::ffi; +use std::{marker::PhantomData, ptr}; + use foreign_types::ForeignTypeRef; -use std::marker::PhantomData; -use std::ptr; -use crate::error::ErrorStack; -use crate::pkey::{HasPrivate, HasPublic, PKeyRef}; -use crate::{cvt, cvt_p}; +use crate::{ + cvt, cvt_p, + error::ErrorStack, + ffi, + pkey::{HasPrivate, HasPublic, PKeyRef}, +}; /// A type used to derive a shared secret between two keys. pub struct Deriver<'a>(*mut ffi::EVP_PKEY_CTX, PhantomData<&'a ()>); @@ -104,10 +106,11 @@ impl<'a> Deriver<'a> { #[cfg(test)] mod test { use super::*; - - use crate::ec::{EcGroup, EcKey}; - use crate::nid::Nid; - use crate::pkey::PKey; + use crate::{ + ec::{EcGroup, EcKey}, + nid::Nid, + pkey::PKey, + }; #[test] fn derive_without_peer() { diff --git a/crates/boring/src/dh.rs b/crates/boring/src/dh.rs index 96a8c63d6..afff8fb6b 100644 --- a/crates/boring/src/dh.rs +++ b/crates/boring/src/dh.rs @@ -1,12 +1,14 @@ -use crate::error::ErrorStack; -use crate::ffi; +use std::{mem, ptr}; + use foreign_types::{ForeignType, ForeignTypeRef}; -use std::mem; -use std::ptr; -use crate::bn::BigNum; -use crate::pkey::{HasParams, Params}; -use crate::{cvt, cvt_p}; +use crate::{ + bn::BigNum, + cvt, cvt_p, + error::ErrorStack, + ffi, + pkey::{HasParams, Params}, +}; generic_foreign_type_and_impl_send_sync! { type CType = ffi::DH; @@ -84,9 +86,11 @@ use crate::ffi::DH_set0_pqg; #[cfg(test)] mod tests { - use crate::bn::BigNum; - use crate::dh::Dh; - use crate::ssl::{SslContext, SslMethod}; + use crate::{ + bn::BigNum, + dh::Dh, + ssl::{SslContext, SslMethod}, + }; #[test] fn test_dh() { diff --git a/crates/boring/src/dsa.rs b/crates/boring/src/dsa.rs index 60c0c1970..0a8b57016 100644 --- a/crates/boring/src/dsa.rs +++ b/crates/boring/src/dsa.rs @@ -5,17 +5,18 @@ //! using the private key that can be validated with the public key but not be generated //! without the private key. -use crate::ffi; +use std::{fmt, mem, ptr}; + use foreign_types::{ForeignType, ForeignTypeRef}; use libc::c_uint; -use std::fmt; -use std::mem; -use std::ptr; - -use crate::bn::{BigNum, BigNumRef}; -use crate::error::ErrorStack; -use crate::pkey::{HasParams, HasPrivate, HasPublic, Private, Public}; -use crate::{cvt, cvt_p}; + +use crate::{ + bn::{BigNum, BigNumRef}, + cvt, cvt_p, + error::ErrorStack, + ffi, + pkey::{HasParams, HasPrivate, HasPublic, Private, Public}, +}; generic_foreign_type_and_impl_send_sync! { type CType = ffi::DSA; diff --git a/crates/boring/src/ec.rs b/crates/boring/src/ec.rs index 9c80b2de5..395de0b99 100644 --- a/crates/boring/src/ec.rs +++ b/crates/boring/src/ec.rs @@ -15,17 +15,19 @@ //! [`EcGroup`]: struct.EcGroup.html //! [`Nid`]: ../nid/struct.Nid.html //! [Eliptic Curve Cryptography]: https://wiki.openssl.org/index.php/Elliptic_Curve_Cryptography -use crate::ffi; +use std::{fmt, ptr}; + use foreign_types::{ForeignType, ForeignTypeRef}; use libc::c_int; -use std::fmt; -use std::ptr; -use crate::bn::{BigNumContextRef, BigNumRef}; -use crate::error::ErrorStack; -use crate::nid::Nid; -use crate::pkey::{HasParams, HasPrivate, HasPublic, Params, Private, Public}; -use crate::{cvt, cvt_n, cvt_p, init}; +use crate::{ + bn::{BigNumContextRef, BigNumRef}, + cvt, cvt_n, cvt_p, + error::ErrorStack, + ffi, init, + nid::Nid, + pkey::{HasParams, HasPrivate, HasPublic, Params, Private, Public}, +}; /// Compressed or Uncompressed conversion /// @@ -864,8 +866,10 @@ mod test { use hex::FromHex; use super::*; - use crate::bn::{BigNum, BigNumContext}; - use crate::nid::Nid; + use crate::{ + bn::{BigNum, BigNumContext}, + nid::Nid, + }; #[test] fn key_new_by_curve_name() { diff --git a/crates/boring/src/ecdsa.rs b/crates/boring/src/ecdsa.rs index 0d333003c..9d0ff33df 100644 --- a/crates/boring/src/ecdsa.rs +++ b/crates/boring/src/ecdsa.rs @@ -1,16 +1,18 @@ //! Low level Elliptic Curve Digital Signature Algorithm (ECDSA) functions. -use crate::ffi; +use std::{mem, ptr}; + use foreign_types::{ForeignType, ForeignTypeRef}; use libc::{c_int, size_t}; -use std::mem; -use std::ptr; -use crate::bn::{BigNum, BigNumRef}; -use crate::ec::EcKeyRef; -use crate::error::ErrorStack; -use crate::pkey::{HasPrivate, HasPublic}; -use crate::{cvt_n, cvt_p}; +use crate::{ + bn::{BigNum, BigNumRef}, + cvt_n, cvt_p, + ec::EcKeyRef, + error::ErrorStack, + ffi, + pkey::{HasPrivate, HasPublic}, +}; foreign_type_and_impl_send_sync! { type CType = ffi::ECDSA_SIG; diff --git a/crates/boring/src/error.rs b/crates/boring/src/error.rs index fdc3ba9ef..1488a8425 100644 --- a/crates/boring/src/error.rs +++ b/crates/boring/src/error.rs @@ -15,14 +15,9 @@ //! Err(e) => println!("Parsing Error: {:?}", e), //! } //! ``` +use std::{borrow::Cow, error, ffi::CStr, fmt, io, ptr, str}; + use libc::{c_char, c_uint}; -use std::borrow::Cow; -use std::error; -use std::ffi::CStr; -use std::fmt; -use std::io; -use std::ptr; -use std::str; use crate::ffi; diff --git a/crates/boring/src/ex_data.rs b/crates/boring/src/ex_data.rs index d4f002129..5d551b054 100644 --- a/crates/boring/src/ex_data.rs +++ b/crates/boring/src/ex_data.rs @@ -1,6 +1,7 @@ -use libc::c_int; use std::marker::PhantomData; +use libc::c_int; + /// A slot in a type's "extra data" structure. /// /// It is parameterized over the type containing the extra data as well as the diff --git a/crates/boring/src/hash.rs b/crates/boring/src/hash.rs index 5690a05a7..5e991bff6 100644 --- a/crates/boring/src/hash.rs +++ b/crates/boring/src/hash.rs @@ -1,15 +1,18 @@ -use crate::ffi; -use std::convert::TryInto; -use std::fmt; -use std::io; -use std::io::prelude::*; -use std::ops::{Deref, DerefMut}; -use std::ptr; - -use crate::error::ErrorStack; -use crate::ffi::{EVP_MD_CTX_free, EVP_MD_CTX_new}; -use crate::nid::Nid; -use crate::{cvt, cvt_p}; +use std::{ + convert::TryInto, + fmt, io, + io::prelude::*, + ops::{Deref, DerefMut}, + ptr, +}; + +use crate::{ + cvt, cvt_p, + error::ErrorStack, + ffi, + ffi::{EVP_MD_CTX_free, EVP_MD_CTX_new}, + nid::Nid, +}; #[derive(Copy, Clone, PartialEq, Eq)] pub struct MessageDigest(*const ffi::EVP_MD); @@ -335,9 +338,10 @@ pub fn hash_xof(t: MessageDigest, data: &[u8], buf: &mut [u8]) -> Result<(), Err #[cfg(test)] mod tests { - use hex::{self, FromHex}; use std::io::prelude::*; + use hex::{self, FromHex}; + use super::*; fn hash_test(hashtype: MessageDigest, hashtest: &(&str, &str)) { diff --git a/crates/boring/src/lib.rs b/crates/boring/src/lib.rs index ae8dff0be..0895b8f8e 100644 --- a/crates/boring/src/lib.rs +++ b/crates/boring/src/lib.rs @@ -99,12 +99,11 @@ extern crate libc; #[cfg(test)] extern crate hex; -#[doc(inline)] -pub use crate::ffi::init; - use libc::{c_int, size_t}; use crate::error::ErrorStack; +#[doc(inline)] +pub use crate::ffi::init; #[macro_use] mod macros; diff --git a/crates/boring/src/memcmp.rs b/crates/boring/src/memcmp.rs index c00b55409..24978c38a 100644 --- a/crates/boring/src/memcmp.rs +++ b/crates/boring/src/memcmp.rs @@ -29,9 +29,10 @@ //! assert!(!eq(&a, &b)); //! assert!(!eq(&a, &c)); //! ``` -use crate::ffi; use libc::size_t; +use crate::ffi; + /// Returns `true` iff `a` and `b` contain the same bytes. /// /// This operation takes an amount of time dependent on the length of the two diff --git a/crates/boring/src/nid.rs b/crates/boring/src/nid.rs index 8f9b400a4..1bf2285e1 100644 --- a/crates/boring/src/nid.rs +++ b/crates/boring/src/nid.rs @@ -1,12 +1,9 @@ //! A collection of numerical identifiers for OpenSSL objects. -use crate::ffi; -use libc::{c_char, c_int}; +use std::{ffi::CStr, str}; -use std::ffi::CStr; -use std::str; +use libc::{c_char, c_int}; -use crate::cvt_p; -use crate::error::ErrorStack; +use crate::{cvt_p, error::ErrorStack, ffi}; /// The digest and public-key algorithms associated with a signature. pub struct SignatureAlgorithms { diff --git a/crates/boring/src/pkcs12.rs b/crates/boring/src/pkcs12.rs index 4caec029a..f46de48e9 100644 --- a/crates/boring/src/pkcs12.rs +++ b/crates/boring/src/pkcs12.rs @@ -1,17 +1,19 @@ //! PKCS #12 archives. -use crate::ffi; +use std::{ffi::CString, ptr}; + use foreign_types::{ForeignType, ForeignTypeRef}; use libc::c_int; -use std::ffi::CString; -use std::ptr; -use crate::error::ErrorStack; -use crate::nid::Nid; -use crate::pkey::{HasPrivate, PKey, PKeyRef, Private}; -use crate::stack::Stack; -use crate::x509::{X509Ref, X509}; -use crate::{cvt_0i, cvt_p}; +use crate::{ + cvt_0i, cvt_p, + error::ErrorStack, + ffi, + nid::Nid, + pkey::{HasPrivate, PKey, PKeyRef, Private}, + stack::Stack, + x509::{X509Ref, X509}, +}; pub const PKCS12_DEFAULT_ITER: c_int = 2048; @@ -202,17 +204,17 @@ impl Pkcs12Builder { #[cfg(test)] mod test { - use crate::hash::MessageDigest; use hex; - use crate::asn1::Asn1Time; - use crate::nid::Nid; - use crate::pkey::PKey; - use crate::rsa::Rsa; - use crate::x509::extension::KeyUsage; - use crate::x509::{X509Name, X509}; - use super::*; + use crate::{ + asn1::Asn1Time, + hash::MessageDigest, + nid::Nid, + pkey::PKey, + rsa::Rsa, + x509::{extension::KeyUsage, X509Name, X509}, + }; #[test] fn parse() { diff --git a/crates/boring/src/pkcs5.rs b/crates/boring/src/pkcs5.rs index 8503d0b34..a9022555f 100644 --- a/crates/boring/src/pkcs5.rs +++ b/crates/boring/src/pkcs5.rs @@ -1,11 +1,8 @@ -use crate::ffi; -use libc::{c_int, c_uint}; use std::ptr; -use crate::cvt; -use crate::error::ErrorStack; -use crate::hash::MessageDigest; -use crate::symm::Cipher; +use libc::{c_int, c_uint}; + +use crate::{cvt, error::ErrorStack, ffi, hash::MessageDigest, symm::Cipher}; #[derive(Clone, Eq, PartialEq, Hash, Debug)] pub struct KeyIvPair { @@ -139,8 +136,7 @@ pub fn scrypt( #[cfg(test)] mod tests { - use crate::hash::MessageDigest; - use crate::symm::Cipher; + use crate::{hash::MessageDigest, symm::Cipher}; // Test vectors from // https://git.lysator.liu.se/nettle/nettle/blob/nettle_3.1.1_release_20150424/testsuite/pbkdf2-test.c diff --git a/crates/boring/src/pkey.rs b/crates/boring/src/pkey.rs index 0975b3955..7135d7a71 100644 --- a/crates/boring/src/pkey.rs +++ b/crates/boring/src/pkey.rs @@ -40,22 +40,22 @@ //! println!("{:?}", str::from_utf8(pub_key.as_slice()).unwrap()); //! ``` -use crate::ffi; +use std::{ffi::CString, fmt, mem, ptr}; + use foreign_types::{ForeignType, ForeignTypeRef}; use libc::{c_int, c_long}; -use std::ffi::CString; -use std::fmt; -use std::mem; -use std::ptr; - -use crate::bio::MemBioSlice; -use crate::dh::Dh; -use crate::dsa::Dsa; -use crate::ec::EcKey; -use crate::error::ErrorStack; -use crate::rsa::Rsa; -use crate::util::{invoke_passwd_cb, CallbackState}; -use crate::{cvt, cvt_p}; + +use crate::{ + bio::MemBioSlice, + cvt, cvt_p, + dh::Dh, + dsa::Dsa, + ec::EcKey, + error::ErrorStack, + ffi, + rsa::Rsa, + util::{invoke_passwd_cb, CallbackState}, +}; /// A tag type indicating that a key only has parameters. pub enum Params {} @@ -505,12 +505,8 @@ use crate::ffi::EVP_PKEY_up_ref; #[cfg(test)] mod tests { - use crate::ec::EcKey; - use crate::nid::Nid; - use crate::rsa::Rsa; - use crate::symm::Cipher; - use super::*; + use crate::{ec::EcKey, nid::Nid, rsa::Rsa, symm::Cipher}; #[test] fn test_to_password() { diff --git a/crates/boring/src/rand.rs b/crates/boring/src/rand.rs index c551b2f9e..474693c68 100644 --- a/crates/boring/src/rand.rs +++ b/crates/boring/src/rand.rs @@ -10,11 +10,9 @@ //! let mut buf = [0; 256]; //! rand_bytes(&mut buf).unwrap(); //! ``` -use crate::ffi; use libc::c_int; -use crate::cvt; -use crate::error::ErrorStack; +use crate::{cvt, error::ErrorStack, ffi}; /// Fill buffer with cryptographically strong pseudo-random bytes. /// diff --git a/crates/boring/src/rsa.rs b/crates/boring/src/rsa.rs index c17b36a3b..324ac32f8 100644 --- a/crates/boring/src/rsa.rs +++ b/crates/boring/src/rsa.rs @@ -23,17 +23,18 @@ //! let mut buf = vec![0; rsa.size() as usize]; //! let encrypted_len = rsa.public_encrypt(data, &mut buf, Padding::PKCS1).unwrap(); //! ``` -use crate::ffi; +use std::{fmt, mem, ptr}; + use foreign_types::{ForeignType, ForeignTypeRef}; use libc::c_int; -use std::fmt; -use std::mem; -use std::ptr; -use crate::bn::{BigNum, BigNumRef}; -use crate::error::ErrorStack; -use crate::pkey::{HasPrivate, HasPublic, Private, Public}; -use crate::{cvt, cvt_n, cvt_p}; +use crate::{ + bn::{BigNum, BigNumRef}, + cvt, cvt_n, cvt_p, + error::ErrorStack, + ffi, + pkey::{HasPrivate, HasPublic, Private, Public}, +}; pub const EVP_PKEY_OP_SIGN: c_int = 1 << 3; pub const EVP_PKEY_OP_VERIFY: c_int = 1 << 4; @@ -698,9 +699,8 @@ use crate::ffi::{ #[cfg(test)] mod test { - use crate::symm::Cipher; - use super::*; + use crate::symm::Cipher; #[test] fn test_from_password() { diff --git a/crates/boring/src/sha.rs b/crates/boring/src/sha.rs index 98aa26ba9..6fc29dd21 100644 --- a/crates/boring/src/sha.rs +++ b/crates/boring/src/sha.rs @@ -43,10 +43,12 @@ //! println!("Hash = {}", hex::encode(hash)); //! } //! ``` -use crate::ffi; -use libc::c_void; use std::mem::MaybeUninit; +use libc::c_void; + +use crate::ffi; + /// Computes the SHA1 hash of some data. /// /// # Warning diff --git a/crates/boring/src/sign.rs b/crates/boring/src/sign.rs index 5bfa2bc44..46a624393 100644 --- a/crates/boring/src/sign.rs +++ b/crates/boring/src/sign.rs @@ -34,20 +34,24 @@ //! verifier.update(data2).unwrap(); //! assert!(verifier.verify(&signature).unwrap()); //! ``` -use crate::ffi; +use std::{ + io::{self, Write}, + marker::PhantomData, + ptr, +}; + use foreign_types::ForeignTypeRef; use libc::c_int; -use std::io::{self, Write}; -use std::marker::PhantomData; -use std::ptr; - -use crate::error::ErrorStack; -use crate::hash::MessageDigest; -use crate::pkey::{HasPrivate, HasPublic, PKeyRef}; -use crate::rsa::Padding; -use crate::{cvt, cvt_p}; -use crate::ffi::{EVP_MD_CTX_free, EVP_MD_CTX_new}; +use crate::{ + cvt, cvt_p, + error::ErrorStack, + ffi, + ffi::{EVP_MD_CTX_free, EVP_MD_CTX_new}, + hash::MessageDigest, + pkey::{HasPrivate, HasPublic, PKeyRef}, + rsa::Padding, +}; /// Salt lengths that must be used with `set_rsa_pss_saltlen`. pub struct RsaPssSaltlen(c_int); @@ -575,15 +579,17 @@ use crate::ffi::EVP_DigestVerifyFinal; #[cfg(test)] mod test { - use super::RsaPssSaltlen; use hex::{self, FromHex}; - use crate::ec::{EcGroup, EcKey}; - use crate::hash::MessageDigest; - use crate::nid::Nid; - use crate::pkey::PKey; - use crate::rsa::{Padding, Rsa}; - use crate::sign::{Signer, Verifier}; + use super::RsaPssSaltlen; + use crate::{ + ec::{EcGroup, EcKey}, + hash::MessageDigest, + nid::Nid, + pkey::PKey, + rsa::{Padding, Rsa}, + sign::{Signer, Verifier}, + }; const INPUT: &str = "65794a68624763694f694a53557a49314e694a392e65794a7063334d694f694a71623255694c41304b49434a6c\ diff --git a/crates/boring/src/srtp.rs b/crates/boring/src/srtp.rs index 84deca9e3..d0b30aae5 100644 --- a/crates/boring/src/srtp.rs +++ b/crates/boring/src/srtp.rs @@ -1,9 +1,9 @@ -use crate::ffi; -use crate::stack::Stackable; +use std::{ffi::CStr, str}; + use foreign_types::ForeignTypeRef; use libc::c_ulong; -use std::ffi::CStr; -use std::str; + +use crate::{ffi, stack::Stackable}; /// fake free method, since SRTP_PROTECTION_PROFILE is static unsafe fn free(_profile: *mut ffi::SRTP_PROTECTION_PROFILE) {} diff --git a/crates/boring/src/ssl/bio.rs b/crates/boring/src/ssl/bio.rs index b88fdaff0..d50e78f5d 100644 --- a/crates/boring/src/ssl/bio.rs +++ b/crates/boring/src/ssl/bio.rs @@ -1,17 +1,21 @@ -use crate::ffi::{ - self, BIO_clear_retry_flags, BIO_new, BIO_set_retry_read, BIO_set_retry_write, BIO, - BIO_CTRL_DGRAM_QUERY_MTU, BIO_CTRL_FLUSH, +use std::{ + any::Any, + io, + io::prelude::*, + panic::{catch_unwind, AssertUnwindSafe}, + ptr, slice, }; + use libc::{c_char, c_int, c_long, c_void, strlen}; -use std::any::Any; -use std::io; -use std::io::prelude::*; -use std::panic::{catch_unwind, AssertUnwindSafe}; -use std::ptr; -use std::slice; - -use crate::cvt_p; -use crate::error::ErrorStack; + +use crate::{ + cvt_p, + error::ErrorStack, + ffi::{ + self, BIO_clear_retry_flags, BIO_new, BIO_set_retry_read, BIO_set_retry_write, BIO, + BIO_CTRL_DGRAM_QUERY_MTU, BIO_CTRL_FLUSH, + }, +}; pub struct StreamState { pub stream: S, diff --git a/crates/boring/src/ssl/callbacks.rs b/crates/boring/src/ssl/callbacks.rs index c4bcb3f05..bdeb99bda 100644 --- a/crates/boring/src/ssl/callbacks.rs +++ b/crates/boring/src/ssl/callbacks.rs @@ -1,22 +1,20 @@ #![forbid(unsafe_op_in_unsafe_fn)] +use std::{ffi::CStr, ptr, slice, str, sync::Arc}; + +use foreign_types::{ForeignType, ForeignTypeRef}; +use libc::{c_char, c_int, c_uchar, c_uint, c_void}; + use super::{ AlpnError, ClientHello, GetSessionPendingError, PrivateKeyMethod, PrivateKeyMethodError, SelectCertError, SniError, Ssl, SslAlert, SslContext, SslContextRef, SslRef, SslSession, SslSessionRef, SslSignatureAlgorithm, SESSION_CTX_INDEX, }; -use crate::error::ErrorStack; -use crate::ffi; -use crate::x509::{X509StoreContext, X509StoreContextRef}; -use foreign_types::ForeignType; -use foreign_types::ForeignTypeRef; -use libc::c_char; -use libc::{c_int, c_uchar, c_uint, c_void}; -use std::ffi::CStr; -use std::ptr; -use std::slice; -use std::str; -use std::sync::Arc; +use crate::{ + error::ErrorStack, + ffi, + x509::{X509StoreContext, X509StoreContextRef}, +}; pub extern "C" fn raw_verify(preverify_ok: c_int, x509_ctx: *mut ffi::X509_STORE_CTX) -> c_int where diff --git a/crates/boring/src/ssl/connector.rs b/crates/boring/src/ssl/connector.rs index e910a324b..39cdba7b3 100644 --- a/crates/boring/src/ssl/connector.rs +++ b/crates/boring/src/ssl/connector.rs @@ -1,16 +1,19 @@ -use std::io::{Read, Write}; -use std::ops::{Deref, DerefMut}; - -use crate::dh::Dh; -use crate::error::ErrorStack; -use crate::ssl::{ - HandshakeError, Ssl, SslContext, SslContextBuilder, SslContextRef, SslMethod, SslMode, - SslOptions, SslRef, SslStream, SslVerifyMode, +use std::{ + io::{Read, Write}, + net::IpAddr, + ops::{Deref, DerefMut}, }; -use crate::version; -use std::net::IpAddr; use super::MidHandshakeSslStream; +use crate::{ + dh::Dh, + error::ErrorStack, + ssl::{ + HandshakeError, Ssl, SslContext, SslContextBuilder, SslContextRef, SslMethod, SslMode, + SslOptions, SslRef, SslStream, SslVerifyMode, + }, + version, +}; const FFDHE_2048: &str = " -----BEGIN DH PARAMETERS----- diff --git a/crates/boring/src/ssl/error.rs b/crates/boring/src/ssl/error.rs index 0f75b8184..a345525a7 100644 --- a/crates/boring/src/ssl/error.rs +++ b/crates/boring/src/ssl/error.rs @@ -1,12 +1,8 @@ -use crate::ffi; +use std::{error, error::Error as StdError, fmt, io}; + use libc::c_int; -use std::error; -use std::error::Error as StdError; -use std::fmt; -use std::io; -use crate::error::ErrorStack; -use crate::ssl::MidHandshakeSslStream; +use crate::{error::ErrorStack, ffi, ssl::MidHandshakeSslStream}; /// An error code returned from SSL functions. #[derive(Debug, Copy, Clone, PartialEq, Eq)] diff --git a/crates/boring/src/ssl/mod.rs b/crates/boring/src/ssl/mod.rs index 84d0adb08..0fbe6aa0d 100644 --- a/crates/boring/src/ssl/mod.rs +++ b/crates/boring/src/ssl/mod.rs @@ -57,50 +57,52 @@ //! } //! } //! ``` -use crate::ffi; +use std::{ + any::TypeId, + cmp, + collections::HashMap, + convert::TryInto, + ffi::{CStr, CString}, + fmt, io, + io::prelude::*, + marker::PhantomData, + mem::{self, ManuallyDrop}, + ops::{Deref, DerefMut}, + panic::resume_unwind, + path::Path, + ptr::{self, NonNull}, + slice, str, + sync::{Arc, Mutex}, +}; + use foreign_types::{ForeignType, ForeignTypeRef, Opaque}; use libc::{c_char, c_int, c_long, c_uchar, c_uint, c_void}; use once_cell::sync::Lazy; -use std::any::TypeId; -use std::cmp; -use std::collections::HashMap; -use std::convert::TryInto; -use std::ffi::{CStr, CString}; -use std::fmt; -use std::io; -use std::io::prelude::*; -use std::marker::PhantomData; -use std::mem::{self, ManuallyDrop}; -use std::ops::{Deref, DerefMut}; -use std::panic::resume_unwind; -use std::path::Path; -use std::ptr::{self, NonNull}; -use std::slice; -use std::str; -use std::sync::{Arc, Mutex}; - -use crate::dh::DhRef; -use crate::ec::EcKeyRef; -use crate::error::ErrorStack; -use crate::ex_data::Index; -use crate::nid::Nid; -use crate::pkey::{HasPrivate, PKeyRef, Params, Private}; -use crate::srtp::{SrtpProtectionProfile, SrtpProtectionProfileRef}; -use crate::ssl::bio::BioMethod; -use crate::ssl::callbacks::*; -use crate::ssl::error::InnerError; -use crate::stack::{Stack, StackRef}; -use crate::x509::store::{X509Store, X509StoreBuilderRef, X509StoreRef}; -use crate::x509::verify::X509VerifyParamRef; -use crate::x509::{ - X509Name, X509Ref, X509StoreContextRef, X509VerifyError, X509VerifyResult, X509, -}; -use crate::{cvt, cvt_0i, cvt_n, cvt_p, init}; -pub use crate::ssl::connector::{ - ConnectConfiguration, SslAcceptor, SslAcceptorBuilder, SslConnector, SslConnectorBuilder, +pub use crate::ssl::{ + connector::{ + ConnectConfiguration, SslAcceptor, SslAcceptorBuilder, SslConnector, SslConnectorBuilder, + }, + error::{Error, ErrorCode, HandshakeError}, +}; +use crate::{ + cvt, cvt_0i, cvt_n, cvt_p, + dh::DhRef, + ec::EcKeyRef, + error::ErrorStack, + ex_data::Index, + ffi, init, + nid::Nid, + pkey::{HasPrivate, PKeyRef, Params, Private}, + srtp::{SrtpProtectionProfile, SrtpProtectionProfileRef}, + ssl::{bio::BioMethod, callbacks::*, error::InnerError}, + stack::{Stack, StackRef}, + x509::{ + store::{X509Store, X509StoreBuilderRef, X509StoreRef}, + verify::X509VerifyParamRef, + X509Name, X509Ref, X509StoreContextRef, X509VerifyError, X509VerifyResult, X509, + }, }; -pub use crate::ssl::error::{Error, ErrorCode, HandshakeError}; mod bio; mod callbacks; @@ -3941,12 +3943,13 @@ impl PrivateKeyMethodError { pub const RETRY: Self = Self(ffi::ssl_private_key_result_t::ssl_private_key_retry); } -use crate::ffi::{SSL_CTX_up_ref, SSL_SESSION_get_master_key, SSL_SESSION_up_ref, SSL_is_server}; - -use crate::ffi::{DTLS_method, TLS_client_method, TLS_method, TLS_server_method}; - use std::sync::Once; +use crate::ffi::{ + DTLS_method, SSL_CTX_up_ref, SSL_SESSION_get_master_key, SSL_SESSION_up_ref, SSL_is_server, + TLS_client_method, TLS_method, TLS_server_method, +}; + unsafe fn get_new_idx(f: ffi::CRYPTO_EX_free) -> c_int { // hack around https://rt.openssl.org/Ticket/Display.html?id=3710&user=guest&pass=guest static ONCE: Once = Once::new(); diff --git a/crates/boring/src/ssl/test/mod.rs b/crates/boring/src/ssl/test/mod.rs index d3319f5f6..030029297 100644 --- a/crates/boring/src/ssl/test/mod.rs +++ b/crates/boring/src/ssl/test/mod.rs @@ -1,26 +1,28 @@ +use std::{ + io, + io::prelude::*, + mem, + net::{TcpListener, TcpStream}, + path::Path, + sync::atomic::{AtomicBool, Ordering}, + thread, +}; + use hex; -use std::io; -use std::io::prelude::*; -use std::mem; -use std::net::{TcpListener, TcpStream}; -use std::path::Path; -use std::sync::atomic::{AtomicBool, Ordering}; -use std::thread; - -use crate::error::ErrorStack; -use crate::hash::MessageDigest; -use crate::pkey::PKey; -use crate::srtp::SrtpProfileId; -use crate::ssl; -use crate::ssl::test::server::Server; -use crate::ssl::SslVersion; -use crate::ssl::{ - ExtensionType, ShutdownResult, ShutdownState, Ssl, SslAcceptor, SslAcceptorBuilder, - SslConnector, SslContext, SslFiletype, SslMethod, SslOptions, SslStream, SslVerifyMode, + +use crate::{ + error::ErrorStack, + hash::MessageDigest, + pkey::PKey, + srtp::SrtpProfileId, + ssl, + ssl::{ + test::server::Server, ExtensionType, ShutdownResult, ShutdownState, Ssl, SslAcceptor, + SslAcceptorBuilder, SslConnector, SslContext, SslFiletype, SslMethod, SslOptions, + SslStream, SslVerifyMode, SslVersion, + }, + x509::{store::X509StoreBuilder, verify::X509CheckFlags, X509Name, X509}, }; -use crate::x509::store::X509StoreBuilder; -use crate::x509::verify::X509CheckFlags; -use crate::x509::{X509Name, X509}; mod private_key_method; mod server; diff --git a/crates/boring/src/ssl/test/private_key_method.rs b/crates/boring/src/ssl/test/private_key_method.rs index 019a8c7c4..2e6bd96db 100644 --- a/crates/boring/src/ssl/test/private_key_method.rs +++ b/crates/boring/src/ssl/test/private_key_method.rs @@ -1,18 +1,27 @@ +use std::{ + io::Write, + sync::{ + atomic::{AtomicBool, AtomicUsize, Ordering}, + Arc, + }, +}; + use once_cell::sync::OnceCell; -use super::server::{Builder, Server}; -use super::KEY; -use crate::hash::MessageDigest; -use crate::pkey::PKey; -use crate::rsa::Padding; -use crate::sign::{RsaPssSaltlen, Signer}; -use crate::ssl::{ - ErrorCode, HandshakeError, PrivateKeyMethod, PrivateKeyMethodError, SslRef, - SslSignatureAlgorithm, +use super::{ + server::{Builder, Server}, + KEY, +}; +use crate::{ + hash::MessageDigest, + pkey::PKey, + rsa::Padding, + sign::{RsaPssSaltlen, Signer}, + ssl::{ + ErrorCode, HandshakeError, PrivateKeyMethod, PrivateKeyMethodError, SslRef, + SslSignatureAlgorithm, + }, }; -use std::io::Write; -use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering}; -use std::sync::Arc; #[allow(clippy::type_complexity)] pub(super) struct Method { diff --git a/crates/boring/src/ssl/test/server.rs b/crates/boring/src/ssl/test/server.rs index e5c0497c0..e3b8ffd93 100644 --- a/crates/boring/src/ssl/test/server.rs +++ b/crates/boring/src/ssl/test/server.rs @@ -1,6 +1,8 @@ -use std::io::{Read, Write}; -use std::net::{SocketAddr, TcpListener, TcpStream}; -use std::thread::{self, JoinHandle}; +use std::{ + io::{Read, Write}, + net::{SocketAddr, TcpListener, TcpStream}, + thread::{self, JoinHandle}, +}; use crate::ssl::{ HandshakeError, Ssl, SslContext, SslContextBuilder, SslFiletype, SslMethod, SslRef, SslStream, diff --git a/crates/boring/src/ssl/test/session.rs b/crates/boring/src/ssl/test/session.rs index 23c0f4d5d..512ea50a7 100644 --- a/crates/boring/src/ssl/test/session.rs +++ b/crates/boring/src/ssl/test/session.rs @@ -1,11 +1,14 @@ -use std::io::Write; -use std::sync::atomic::{AtomicBool, Ordering}; -use std::sync::OnceLock; +use std::{ + io::Write, + sync::{ + atomic::{AtomicBool, Ordering}, + OnceLock, + }, +}; -use crate::ssl::test::server::Server; use crate::ssl::{ - ErrorCode, GetSessionPendingError, HandshakeError, Ssl, SslContext, SslContextBuilder, - SslMethod, SslOptions, SslSession, SslSessionCacheMode, SslVersion, + test::server::Server, ErrorCode, GetSessionPendingError, HandshakeError, Ssl, SslContext, + SslContextBuilder, SslMethod, SslOptions, SslSession, SslSessionCacheMode, SslVersion, }; #[test] diff --git a/crates/boring/src/stack.rs b/crates/boring/src/stack.rs index 67b952739..e4670e90e 100644 --- a/crates/boring/src/stack.rs +++ b/crates/boring/src/stack.rs @@ -1,21 +1,24 @@ -use crate::ffi; +use std::{ + borrow::Borrow, + convert::AsRef, + fmt, iter, + marker::PhantomData, + mem, + ops::{Deref, DerefMut, Index, IndexMut, Range}, +}; + use foreign_types::{ForeignType, ForeignTypeRef, Opaque}; use libc::size_t; -use std::borrow::Borrow; -use std::convert::AsRef; -use std::fmt; -use std::iter; -use std::marker::PhantomData; -use std::mem; -use std::ops::{Deref, DerefMut, Index, IndexMut, Range}; - -use crate::error::ErrorStack; -use crate::{cvt_0, cvt_p}; - -use crate::ffi::{ - sk_free as OPENSSL_sk_free, sk_new_null as OPENSSL_sk_new_null, sk_num as OPENSSL_sk_num, - sk_pop as OPENSSL_sk_pop, sk_push as OPENSSL_sk_push, sk_value as OPENSSL_sk_value, - _STACK as OPENSSL_STACK, + +use crate::{ + cvt_0, cvt_p, + error::ErrorStack, + ffi, + ffi::{ + sk_free as OPENSSL_sk_free, sk_new_null as OPENSSL_sk_new_null, sk_num as OPENSSL_sk_num, + sk_pop as OPENSSL_sk_pop, sk_push as OPENSSL_sk_push, sk_value as OPENSSL_sk_value, + _STACK as OPENSSL_STACK, + }, }; /// Trait implemented by types which can be placed in a stack. diff --git a/crates/boring/src/string.rs b/crates/boring/src/string.rs index 05c401f16..fa3259fc9 100644 --- a/crates/boring/src/string.rs +++ b/crates/boring/src/string.rs @@ -1,13 +1,9 @@ -use crate::ffi; +use std::{convert::AsRef, ffi::CStr, fmt, ops::Deref, str}; + use foreign_types::ForeignTypeRef; use libc::{c_char, c_void}; -use std::convert::AsRef; -use std::ffi::CStr; -use std::fmt; -use std::ops::Deref; -use std::str; -use crate::stack::Stackable; +use crate::{ffi, stack::Stackable}; foreign_type_and_impl_send_sync! { type CType = c_char; diff --git a/crates/boring/src/symm.rs b/crates/boring/src/symm.rs index 55a60999d..adc6bb7e7 100644 --- a/crates/boring/src/symm.rs +++ b/crates/boring/src/symm.rs @@ -52,14 +52,11 @@ //! println!("Decrypted: '{}'", output_string); //! ``` -use crate::ffi; +use std::{cmp, ptr}; + use libc::{c_int, c_uint}; -use std::cmp; -use std::ptr; -use crate::error::ErrorStack; -use crate::nid::Nid; -use crate::{cvt, cvt_p}; +use crate::{cvt, cvt_p, error::ErrorStack, ffi, nid::Nid}; #[derive(Copy, Clone)] pub enum Mode { @@ -688,9 +685,10 @@ use crate::ffi::{EVP_CIPHER_block_size, EVP_CIPHER_iv_length, EVP_CIPHER_key_len #[cfg(test)] mod tests { - use super::*; use hex::{self, FromHex}; + use super::*; + #[test] fn test_stream_cipher_output() { let key = [0u8; 16]; diff --git a/crates/boring/src/util.rs b/crates/boring/src/util.rs index 21591d2f4..d560377e5 100644 --- a/crates/boring/src/util.rs +++ b/crates/boring/src/util.rs @@ -1,7 +1,10 @@ +use std::{ + any::Any, + panic::{self, AssertUnwindSafe}, + slice, +}; + use libc::{c_char, c_int, c_void}; -use std::any::Any; -use std::panic::{self, AssertUnwindSafe}; -use std::slice; use crate::error::ErrorStack; diff --git a/crates/boring/src/x509/extension.rs b/crates/boring/src/x509/extension.rs index 639d28bd2..4ef0a8194 100644 --- a/crates/boring/src/x509/extension.rs +++ b/crates/boring/src/x509/extension.rs @@ -17,12 +17,15 @@ //! ``` use std::fmt::Write; -use crate::asn1::Asn1Object; -use crate::error::ErrorStack; -use crate::nid::Nid; -use crate::x509::{GeneralName, Stack, X509Extension, X509v3Context}; use foreign_types::ForeignType; +use crate::{ + asn1::Asn1Object, + error::ErrorStack, + nid::Nid, + x509::{GeneralName, Stack, X509Extension, X509v3Context}, +}; + /// An extension which indicates whether a certificate is a CA certificate. pub struct BasicConstraints { critical: bool, diff --git a/crates/boring/src/x509/mod.rs b/crates/boring/src/x509/mod.rs index 30b5e725b..e870d8e4c 100644 --- a/crates/boring/src/x509/mod.rs +++ b/crates/boring/src/x509/mod.rs @@ -7,36 +7,39 @@ //! Internet protocols, including SSL/TLS, which is the basis for HTTPS, //! the secure protocol for browsing the web. -use crate::ffi; +use std::{ + convert::TryInto, + error::Error, + ffi::{CStr, CString}, + fmt, + marker::PhantomData, + mem, + net::IpAddr, + path::Path, + ptr, slice, str, +}; + use foreign_types::{ForeignType, ForeignTypeRef}; use libc::{c_int, c_long, c_void}; -use std::convert::TryInto; -use std::error::Error; -use std::ffi::{CStr, CString}; -use std::fmt; -use std::marker::PhantomData; -use std::mem; -use std::net::IpAddr; -use std::path::Path; -use std::ptr; -use std::slice; -use std::str; - -use crate::asn1::{ - Asn1BitStringRef, Asn1IntegerRef, Asn1Object, Asn1ObjectRef, Asn1StringRef, Asn1TimeRef, - Asn1Type, + +use crate::{ + asn1::{ + Asn1BitStringRef, Asn1IntegerRef, Asn1Object, Asn1ObjectRef, Asn1StringRef, Asn1TimeRef, + Asn1Type, + }, + bio::MemBioSlice, + conf::ConfRef, + cvt, cvt_n, cvt_p, + error::ErrorStack, + ex_data::Index, + ffi, + hash::{DigestBytes, MessageDigest}, + nid::Nid, + pkey::{HasPrivate, HasPublic, PKey, PKeyRef, Public}, + ssl::SslRef, + stack::{Stack, StackRef, Stackable}, + string::OpensslString, }; -use crate::bio::MemBioSlice; -use crate::conf::ConfRef; -use crate::error::ErrorStack; -use crate::ex_data::Index; -use crate::hash::{DigestBytes, MessageDigest}; -use crate::nid::Nid; -use crate::pkey::{HasPrivate, HasPublic, PKey, PKeyRef, Public}; -use crate::ssl::SslRef; -use crate::stack::{Stack, StackRef, Stackable}; -use crate::string::OpensslString; -use crate::{cvt, cvt_n, cvt_p}; pub mod extension; pub mod store; @@ -1684,15 +1687,12 @@ impl Stackable for X509Object { type StackType = ffi::stack_st_X509_OBJECT; } -use crate::ffi::{X509_get0_signature, X509_getm_notAfter, X509_getm_notBefore, X509_up_ref}; - use crate::ffi::{ - ASN1_STRING_get0_data, X509_ALGOR_get0, X509_REQ_get_subject_name, X509_REQ_get_version, - X509_STORE_CTX_get0_chain, X509_set1_notAfter, X509_set1_notBefore, + ASN1_STRING_get0_data, X509_ALGOR_get0, X509_OBJECT_get0_X509, X509_REQ_get_subject_name, + X509_REQ_get_version, X509_STORE_CTX_get0_chain, X509_get0_signature, X509_getm_notAfter, + X509_getm_notBefore, X509_set1_notAfter, X509_set1_notBefore, X509_up_ref, }; -use crate::ffi::X509_OBJECT_get0_X509; - #[allow(bad_style)] unsafe fn X509_OBJECT_free(x: *mut ffi::X509_OBJECT) { ffi::X509_OBJECT_free_contents(x); diff --git a/crates/boring/src/x509/store.rs b/crates/boring/src/x509/store.rs index 7033450a3..f6339e404 100644 --- a/crates/boring/src/x509/store.rs +++ b/crates/boring/src/x509/store.rs @@ -40,14 +40,17 @@ //! let store: X509Store = builder.build(); //! ``` -use crate::ffi; -use foreign_types::{ForeignType, ForeignTypeRef}; use std::mem; -use crate::error::ErrorStack; -use crate::stack::StackRef; -use crate::x509::{X509Object, X509}; -use crate::{cvt, cvt_p}; +use foreign_types::{ForeignType, ForeignTypeRef}; + +use crate::{ + cvt, cvt_p, + error::ErrorStack, + ffi, + stack::StackRef, + x509::{X509Object, X509}, +}; foreign_type_and_impl_send_sync! { type CType = ffi::X509_STORE; diff --git a/crates/boring/src/x509/tests.rs b/crates/boring/src/x509/tests.rs index e656873d3..e73b93f74 100644 --- a/crates/boring/src/x509/tests.rs +++ b/crates/boring/src/x509/tests.rs @@ -1,18 +1,22 @@ use hex::{self, FromHex}; -use crate::asn1::Asn1Time; -use crate::bn::{BigNum, MsbOption}; -use crate::hash::MessageDigest; -use crate::nid::Nid; -use crate::pkey::{PKey, Private}; -use crate::rsa::Rsa; -use crate::stack::Stack; -use crate::x509::extension::{ - AuthorityKeyIdentifier, BasicConstraints, ExtendedKeyUsage, KeyUsage, SubjectAlternativeName, - SubjectKeyIdentifier, +use crate::{ + asn1::Asn1Time, + bn::{BigNum, MsbOption}, + hash::MessageDigest, + nid::Nid, + pkey::{PKey, Private}, + rsa::Rsa, + stack::Stack, + x509::{ + extension::{ + AuthorityKeyIdentifier, BasicConstraints, ExtendedKeyUsage, KeyUsage, + SubjectAlternativeName, SubjectKeyIdentifier, + }, + store::X509StoreBuilder, + X509Extension, X509Name, X509Req, X509StoreContext, X509, + }, }; -use crate::x509::store::X509StoreBuilder; -use crate::x509::{X509Extension, X509Name, X509Req, X509StoreContext, X509}; fn pkey() -> PKey { let rsa = Rsa::generate(2048).unwrap(); diff --git a/crates/boring/src/x509/verify.rs b/crates/boring/src/x509/verify.rs index 8bc17a587..09955e854 100644 --- a/crates/boring/src/x509/verify.rs +++ b/crates/boring/src/x509/verify.rs @@ -1,10 +1,9 @@ -use crate::ffi; +use std::net::IpAddr; + use foreign_types::ForeignTypeRef; use libc::c_uint; -use std::net::IpAddr; -use crate::cvt; -use crate::error::ErrorStack; +use crate::{cvt, error::ErrorStack, ffi}; bitflags! { /// Flags used to check an `X509` certificate. diff --git a/crates/boringssl-sys/src/lib.rs b/crates/boringssl-sys/src/lib.rs index 5fc4e3c10..4d73d6ed4 100644 --- a/crates/boringssl-sys/src/lib.rs +++ b/crates/boringssl-sys/src/lib.rs @@ -16,8 +16,10 @@ /// And is dual licensed under the terms of both the Apache License, Version 2.0 and /// the MIT license without any additional terms or conditions. use std::convert::TryInto; -use std::ffi::c_void; -use std::os::raw::{c_char, c_int, c_uint, c_ulong}; +use std::{ + ffi::c_void, + os::raw::{c_char, c_int, c_uint, c_ulong}, +}; #[allow(clippy::useless_transmute, clippy::derive_partial_eq_without_eq)] mod generated { @@ -69,8 +71,7 @@ const_fn! { } pub fn init() { - use std::ptr; - use std::sync::Once; + use std::{ptr, sync::Once}; // explicitly initialize to work around https://github.com/openssl/openssl/issues/3505 static INIT: Once = Once::new(); diff --git a/sshpuffin/src/libssh/mod.rs b/sshpuffin/src/libssh/mod.rs index 41cc91dc5..ac028c6b1 100644 --- a/sshpuffin/src/libssh/mod.rs +++ b/sshpuffin/src/libssh/mod.rs @@ -19,7 +19,6 @@ use std::{ }; use log::debug; - use puffin::{ agent::{AgentDescriptor, AgentName, AgentType}, codec::Codec, diff --git a/tlspuffin/src/boringssl/deterministic.rs b/tlspuffin/src/boringssl/deterministic.rs index bf2df790a..9a8644f98 100644 --- a/tlspuffin/src/boringssl/deterministic.rs +++ b/tlspuffin/src/boringssl/deterministic.rs @@ -9,14 +9,22 @@ pub fn reset_rand() { #[cfg(test)] mod tests { - use crate::boringssl::deterministic::reset_rand; - use crate::put_registry::TLS_PUT_REGISTRY; - use crate::tls::seeds::{create_corpus, seed_client_attacker_full_boring}; - use crate::tls::trace_helper::TraceHelper; - use puffin::put::PutOptions; - use puffin::trace::{Action, InputAction, OutputAction, Step, Trace, TraceContext}; use std::fmt::format; + use puffin::{ + put::PutOptions, + trace::{Action, InputAction, OutputAction, Step, Trace, TraceContext}, + }; + + use crate::{ + boringssl::deterministic::reset_rand, + put_registry::TLS_PUT_REGISTRY, + tls::{ + seeds::{create_corpus, seed_client_attacker_full_boring}, + trace_helper::TraceHelper, + }, + }; + // BUG: This test only works in a single threaded cargo test execution #[ignore] #[test] diff --git a/tlspuffin/src/boringssl/mod.rs b/tlspuffin/src/boringssl/mod.rs index 344c95825..a5878e49d 100644 --- a/tlspuffin/src/boringssl/mod.rs +++ b/tlspuffin/src/boringssl/mod.rs @@ -1,18 +1,6 @@ -use crate::{ - boringssl::util::{set_max_protocol_version, static_rsa_cert}, - claims::{ - ClaimData, ClaimDataTranscript, TlsClaim, TranscriptCertificate, TranscriptClientFinished, - TranscriptServerFinished, TranscriptServerHello, - }, - protocol::TLSProtocolBehavior, - put::TlsPutConfig, - put_registry::BORINGSSL_PUT, - static_certs::{ALICE_CERT, ALICE_PRIVATE_KEY, BOB_CERT, BOB_PRIVATE_KEY, EVE_CERT}, - tls::rustls::msgs::{ - deframer::MessageDeframer, - message::{Message, OpaqueMessage}, - }, -}; +use core::ffi::c_void; +use std::{cell::RefCell, io::ErrorKind, rc::Rc}; + use boring::{ error::ErrorStack, ex_data::Index, @@ -20,10 +8,8 @@ use boring::{ x509::{store::X509StoreBuilder, X509}, }; use boringssl_sys::ssl_st; -use core::ffi::c_void; use foreign_types::ForeignTypeRef; -use log::info; -use log::{debug, trace}; +use log::{debug, info, trace}; use puffin::{ agent::{AgentDescriptor, AgentName, AgentType}, error::Error, @@ -33,7 +19,22 @@ use puffin::{ stream::{MemoryStream, Stream}, trace::TraceContext, }; -use std::{cell::RefCell, io::ErrorKind, rc::Rc}; + +use crate::{ + boringssl::util::{set_max_protocol_version, static_rsa_cert}, + claims::{ + ClaimData, ClaimDataTranscript, TlsClaim, TranscriptCertificate, TranscriptClientFinished, + TranscriptServerFinished, TranscriptServerHello, + }, + protocol::TLSProtocolBehavior, + put::TlsPutConfig, + put_registry::BORINGSSL_PUT, + static_certs::{ALICE_CERT, ALICE_PRIVATE_KEY, BOB_CERT, BOB_PRIVATE_KEY, EVE_CERT}, + tls::rustls::msgs::{ + deframer::MessageDeframer, + message::{Message, OpaqueMessage}, + }, +}; #[cfg(feature = "deterministic")] mod deterministic; @@ -41,6 +42,7 @@ mod transcript; mod util; use std::ops::Deref; + use transcript::extract_current_transcript; pub fn new_boringssl_factory() -> Box> { diff --git a/tlspuffin/src/tls/seeds.rs b/tlspuffin/src/tls/seeds.rs index 81ea50e13..d4f74c836 100644 --- a/tlspuffin/src/tls/seeds.rs +++ b/tlspuffin/src/tls/seeds.rs @@ -9,6 +9,7 @@ use puffin::{ trace::{Action, InputAction, OutputAction, Step, Trace}, }; +use super::rustls::msgs::handshake::EncryptedExtensions; use crate::{ query::TlsQueryMatcher, tls::{ @@ -21,8 +22,6 @@ use crate::{ }, }; -use super::rustls::msgs::handshake::EncryptedExtensions; - pub fn seed_successful_client_auth(client: AgentName, server: AgentName) -> Trace { Trace { prior_traces: vec![], From 0d8df9e715e349eca9e8f337351af4ffa6c72f1c Mon Sep 17 00:00:00 2001 From: Tom Gouville Date: Wed, 13 Mar 2024 18:18:37 +0100 Subject: [PATCH 35/63] fix another pb with determinism in openssl --- tlspuffin/src/openssl/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tlspuffin/src/openssl/mod.rs b/tlspuffin/src/openssl/mod.rs index 3a7a6226d..46bd32ab9 100644 --- a/tlspuffin/src/openssl/mod.rs +++ b/tlspuffin/src/openssl/mod.rs @@ -223,7 +223,7 @@ impl Put for OpenSSL { fn determinism_reseed(&mut self) -> Result<(), Error> { #[cfg(feature = "deterministic")] { - determinism_reseed_openssl(); + deterministic::determinism_reseed_openssl(); Ok(()) } #[cfg(not(feature = "deterministic"))] From 699f572a485f4ad2b301db6365adc7ee64e2f5d7 Mon Sep 17 00:00:00 2001 From: Tom Gouville Date: Wed, 13 Mar 2024 18:32:15 +0100 Subject: [PATCH 36/63] improving conditional seed files generation --- tlspuffin/src/tls/seeds.rs | 59 +++++++++++++------------------------- 1 file changed, 20 insertions(+), 39 deletions(-) diff --git a/tlspuffin/src/tls/seeds.rs b/tlspuffin/src/tls/seeds.rs index d4f74c836..f3e536fec 100644 --- a/tlspuffin/src/tls/seeds.rs +++ b/tlspuffin/src/tls/seeds.rs @@ -2148,45 +2148,26 @@ macro_rules! corpus { } pub fn create_corpus() -> Vec<(Trace, &'static str)> { - if cfg!(not(feature = "boringssl-binding")) { - corpus!( - // Full Handshakes - seed_successful: cfg(feature = "tls13"), - seed_successful_with_ccs: cfg(feature = "tls13"), - seed_successful_with_tickets: cfg(feature = "tls13"), - seed_successful12: cfg(not(feature = "tls12-session-resumption")), - seed_successful12_with_tickets: cfg(feature = "tls12-session-resumption"), - // Client Attackers - seed_client_attacker: cfg(feature = "tls13"), - seed_client_attacker_full: cfg(feature = "tls13"), - seed_client_attacker_auth: cfg(all(feature = "tls13", feature = "client-authentication-transcript-extraction")), - seed_client_attacker12: cfg(feature = "tls12"), - // Session resumption - seed_session_resumption_dhe: cfg(all(feature = "tls13", feature = "tls13-session-resumption")), - seed_session_resumption_ke: cfg(all(feature = "tls13", feature = "tls13-session-resumption")), - // Server Attackers - seed_server_attacker_full: cfg(feature = "tls13") - ) - } else { - corpus!( - // Full Handhakes - seed_successful: cfg(feature = "tls13"), - seed_successful_with_ccs: cfg(feature = "tls13"), - seed_successful_with_tickets: cfg(feature = "tls13"), - seed_successful12: cfg(not(feature = "tls12-session-resumption")), - seed_successful12_with_tickets: cfg(feature = "tls12-session-resumption"), - // Client Attackers - seed_client_attacker: cfg(feature = "tls13"), - seed_client_attacker_full_boring: cfg(feature = "tls13"), - seed_client_attacker_auth_boring: cfg(all(feature = "tls13", feature = "client-authentication-transcript-extraction")), - seed_client_attacker12: cfg(feature = "tls12"), - // Session resumption - seed_session_resumption_dhe: cfg(all(feature = "tls13", feature = "tls13-session-resumption")), - seed_session_resumption_ke: cfg(all(feature = "tls13", feature = "tls13-session-resumption")), - // Server Attackers - seed_server_attacker_full: cfg(feature = "tls13") - ) - } + corpus!( + // Full Handshakes + seed_successful: cfg(feature = "tls13"), + seed_successful_with_ccs: cfg(feature = "tls13"), + seed_successful_with_tickets: cfg(feature = "tls13"), + seed_successful12: cfg(not(feature = "tls12-session-resumption")), + seed_successful12_with_tickets: cfg(feature = "tls12-session-resumption"), + // Client Attackers + seed_client_attacker: cfg(feature = "tls13"), + seed_client_attacker_full: cfg(all(feature = "tls13", not(feature = "boringssl-binding"))), + seed_client_attacker_full_boring: cfg(all(feature = "tls13", feature = "boringssl-binding")), + seed_client_attacker_auth: cfg(all(feature = "tls13", feature = "client-authentication-transcript-extraction", not(feature = "boringssl-binding"))), + seed_client_attacker_auth_boring: cfg(all(feature = "tls13", feature = "client-authentication-transcript-extraction", feature = "boringssl-binding")), + seed_client_attacker12: cfg(feature = "tls12"), + // Session resumption + seed_session_resumption_dhe: cfg(all(feature = "tls13", feature = "tls13-session-resumption")), + seed_session_resumption_ke: cfg(all(feature = "tls13", feature = "tls13-session-resumption")), + // Server Attackers + seed_server_attacker_full: cfg(feature = "tls13") + ) } #[cfg(test)] From cfa9270651e02bb39f1903cdcdab6f431567e363 Mon Sep 17 00:00:00 2001 From: Tom Gouville Date: Thu, 14 Mar 2024 16:16:23 +0100 Subject: [PATCH 37/63] restoring deterministic_rand.c which has been altered by a previous merge --- .../openssl-src-111/src/deterministic_rand.c | 34 +++++++++---------- 1 file changed, 16 insertions(+), 18 deletions(-) diff --git a/crates/openssl-src-111/src/deterministic_rand.c b/crates/openssl-src-111/src/deterministic_rand.c index afed38e1d..4dc65cc52 100644 --- a/crates/openssl-src-111/src/deterministic_rand.c +++ b/crates/openssl-src-111/src/deterministic_rand.c @@ -2,19 +2,22 @@ #include #include -static uint64_t seed = 42; +unsigned int tlspuffin_seed = 42; +const unsigned int m = 0xFFFFFFFF; +const unsigned int a = 22695477; +const unsigned int c = 1; #define UNUSED(x) (void)(x) // Seed the RNG. srand() takes an unsigned int, so we just use the first -// sizeof(uint64_t) bytes in the buffer to seed the RNG. +// sizeof(unsigned int) bytes in the buffer to seed the RNG. static int stdlib_rand_seed(const void *buf, int num) { - if (num < sizeof(uint64_t)) + if (num < 1) { return 0; } - seed = *((uint64_t *) buf); + tlspuffin_seed = *((unsigned int *) buf); return 1; } @@ -24,16 +27,13 @@ static int stdlib_rand_bytes(unsigned char *buf, int num) { for (int index = 0; index < num; ++index) { - seed = 6364136223846793005ULL*seed + 1; - buf[index] = seed>>33; + tlspuffin_seed = (a * tlspuffin_seed + c) % m; + buf[index] = tlspuffin_seed % 256; } return 1; } -static void stdlib_rand_cleanup() -{ -} - +static void stdlib_rand_cleanup() {} static int stdlib_rand_add(const void *buf, int num, double add_entropy) { UNUSED(buf); @@ -41,19 +41,17 @@ static int stdlib_rand_add(const void *buf, int num, double add_entropy) UNUSED(add_entropy); return 1; } - static int stdlib_rand_status() { return 1; } -RAND_METHOD stdlib_rand_meth = { - stdlib_rand_seed, - stdlib_rand_bytes, - stdlib_rand_cleanup, - stdlib_rand_add, - stdlib_rand_bytes, - stdlib_rand_status, +RAND_METHOD stdlib_rand_meth = { stdlib_rand_seed, + stdlib_rand_bytes, + stdlib_rand_cleanup, + stdlib_rand_add, + stdlib_rand_bytes, + stdlib_rand_status }; void make_openssl_deterministic() From d346096495d62d4176b2d3a147e478df773380d7 Mon Sep 17 00:00:00 2001 From: Tom Gouville Date: Thu, 14 Mar 2024 16:30:01 +0100 Subject: [PATCH 38/63] fixing openssl randomness unit test (using version from 5f6e43c7f55c6) --- tlspuffin/src/openssl/deterministic.rs | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/tlspuffin/src/openssl/deterministic.rs b/tlspuffin/src/openssl/deterministic.rs index e633cca81..210b1d40e 100644 --- a/tlspuffin/src/openssl/deterministic.rs +++ b/tlspuffin/src/openssl/deterministic.rs @@ -1,6 +1,6 @@ use std::os::raw::c_int; -use log::warn; +use log::{trace, warn}; #[cfg(feature = "deterministic")] extern "C" { @@ -14,7 +14,7 @@ pub fn get_seed() -> u32 { } #[cfg(feature = "deterministic")] pub fn determinism_set_reseed_openssl() { - println!("Making OpenSSL fully deterministic: reset rand and reseed to a constant..."); + trace!("Making OpenSSL fully deterministic: reset rand and reseed to a constant..."); unsafe { make_openssl_deterministic(); } @@ -23,7 +23,7 @@ pub fn determinism_set_reseed_openssl() { #[cfg(feature = "deterministic")] pub fn determinism_reseed_openssl() { - println!(" - Reseed RAND for OpenSSL"); + trace!(" - Reseed RAND for OpenSSL"); unsafe { let mut seed: [u8; 4] = 42u32.to_le().to_ne_bytes(); let buf = seed.as_mut_ptr(); @@ -38,6 +38,8 @@ mod tests { use crate::openssl::deterministic::{determinism_set_reseed_openssl, get_seed}; + #[test] + #[cfg(feature = "openssl111-binding")] #[test] #[cfg(feature = "openssl111-binding")] fn test_openssl_no_randomness_simple() { @@ -46,6 +48,9 @@ mod tests { assert_eq!(get_seed(), 42); let mut buf1 = [0; 2]; rand_bytes(&mut buf1).unwrap(); - assert_eq!(buf1, [183, 96]); + assert_eq!(buf1, [179, 16]); + assert_ne!(get_seed(), 42); + determinism_set_reseed_openssl(); + assert_eq!(get_seed(), 42); } } From cb76c7e9123affcc7b2e9f394de0daedbca3fafe Mon Sep 17 00:00:00 2001 From: Tom Gouville Date: Fri, 15 Mar 2024 13:59:51 +0100 Subject: [PATCH 39/63] fixing deterministic random generator for openssl --- crates/openssl-src-111/src/deterministic_rand.c | 4 ++-- tlspuffin/src/integration_tests/determinism.rs | 7 ++++++- tlspuffin/src/openssl/deterministic.rs | 5 +---- 3 files changed, 9 insertions(+), 7 deletions(-) diff --git a/crates/openssl-src-111/src/deterministic_rand.c b/crates/openssl-src-111/src/deterministic_rand.c index 4dc65cc52..92f63d70f 100644 --- a/crates/openssl-src-111/src/deterministic_rand.c +++ b/crates/openssl-src-111/src/deterministic_rand.c @@ -27,8 +27,8 @@ static int stdlib_rand_bytes(unsigned char *buf, int num) { for (int index = 0; index < num; ++index) { - tlspuffin_seed = (a * tlspuffin_seed + c) % m; - buf[index] = tlspuffin_seed % 256; + tlspuffin_seed = 6364136223846793005ULL*tlspuffin_seed + 1; + buf[index] = tlspuffin_seed>>33; } return 1; } diff --git a/tlspuffin/src/integration_tests/determinism.rs b/tlspuffin/src/integration_tests/determinism.rs index 300bb0c73..c50ac3e52 100644 --- a/tlspuffin/src/integration_tests/determinism.rs +++ b/tlspuffin/src/integration_tests/determinism.rs @@ -6,7 +6,12 @@ use crate::{ }; #[test] -#[cfg(all(feature = "deterministic", feature = "tls13"))] +#[cfg(all( + feature = "deterministic", + feature = "boringssl-binding", + feature = "tls13", + feature = "TODO" +))] // TODO: only passes in mono-thread!! with option `-test-threads=1` fn test_attacker_full_det_recreate() { // Fail without global rand reset and reseed, BEFORE tracecontext are created (at least for OpenSSL)! TLS_PUT_REGISTRY.determinism_set_reseed_all_factories(); diff --git a/tlspuffin/src/openssl/deterministic.rs b/tlspuffin/src/openssl/deterministic.rs index 210b1d40e..14829a287 100644 --- a/tlspuffin/src/openssl/deterministic.rs +++ b/tlspuffin/src/openssl/deterministic.rs @@ -38,17 +38,14 @@ mod tests { use crate::openssl::deterministic::{determinism_set_reseed_openssl, get_seed}; - #[test] - #[cfg(feature = "openssl111-binding")] #[test] #[cfg(feature = "openssl111-binding")] fn test_openssl_no_randomness_simple() { - assert_eq!(get_seed(), 42); determinism_set_reseed_openssl(); assert_eq!(get_seed(), 42); let mut buf1 = [0; 2]; rand_bytes(&mut buf1).unwrap(); - assert_eq!(buf1, [179, 16]); + assert_eq!(buf1, [177, 180]); assert_ne!(get_seed(), 42); determinism_set_reseed_openssl(); assert_eq!(get_seed(), 42); From 9db96c84c94ca158e72224e03d86ef93746881b0 Mon Sep 17 00:00:00 2001 From: Tom Gouville Date: Fri, 22 Mar 2024 10:43:57 +0100 Subject: [PATCH 40/63] adding BoringSSL version 202403 --- Cargo.lock | 63 --------------------------------- crates/boringssl-sys/Cargo.toml | 1 + crates/boringssl-sys/build.rs | 2 ++ tlspuffin/Cargo.toml | 13 +++++++ 4 files changed, 16 insertions(+), 63 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 09a511b94..64ec2ebc3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -218,10 +218,8 @@ dependencies = [ "bitflags 2.4.1", "boringssl-sys", "foreign-types 0.5.0", - "hex", "libc", "once_cell", - "rusty-hook", ] [[package]] @@ -329,15 +327,6 @@ dependencies = [ "winapi", ] -[[package]] -name = "ci_info" -version = "0.10.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "24f638c70e8c5753795cc9a8c07c44da91554a09e4cf11a7326e8161b0a3c45e" -dependencies = [ - "envmnt", -] - [[package]] name = "clang-sys" version = "1.6.1" @@ -727,16 +716,6 @@ dependencies = [ "termcolor", ] -[[package]] -name = "envmnt" -version = "0.8.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2d328fc287c61314c4a61af7cfdcbd7e678e39778488c7cb13ec133ce0f4059" -dependencies = [ - "fsio", - "indexmap", -] - [[package]] name = "erased-serde" version = "0.3.25" @@ -834,12 +813,6 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "aa9a19cbb55df58761df49b23516a86d432839add4af60fc256da840f66ed35b" -[[package]] -name = "fsio" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1fd087255f739f4f1aeea69f11b72f8080e9c2e7645cd06955dad4a178a49e3" - [[package]] name = "futures" version = "0.3.28" @@ -939,15 +912,6 @@ dependencies = [ "version_check", ] -[[package]] -name = "getopts" -version = "0.2.21" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "14dbbfd5c71d70241ecf9e6f13737f7b5ce823821063188d7e46c41d371eebd5" -dependencies = [ - "unicode-width", -] - [[package]] name = "getrandom" version = "0.2.9" @@ -1438,12 +1402,6 @@ dependencies = [ "windows-sys 0.45.0", ] -[[package]] -name = "nias" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab250442c86f1850815b5d268639dff018c0627022bc1940eb2d642ca1ce12f0" - [[package]] name = "nix" version = "0.25.1" @@ -1904,18 +1862,6 @@ version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4f3208ce4d8448b3f3e7d168a73f5e0c43a61e32930de3bceeccedb388b6bf06" -[[package]] -name = "rusty-hook" -version = "0.11.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96cee9be61be7e1cbadd851e58ed7449c29c620f00b23df937cb9cbc04ac21a3" -dependencies = [ - "ci_info", - "getopts", - "nias", - "toml", -] - [[package]] name = "ryu" version = "1.0.13" @@ -2363,15 +2309,6 @@ dependencies = [ "syn 2.0.39", ] -[[package]] -name = "toml" -version = "0.5.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f4f7f0dd8d50a853a531c426359045b1998f04219d88799810762cd4ad314234" -dependencies = [ - "serde", -] - [[package]] name = "tui" version = "0.19.0" diff --git a/crates/boringssl-sys/Cargo.toml b/crates/boringssl-sys/Cargo.toml index 30c0b9dfb..4ec5cbab2 100644 --- a/crates/boringssl-sys/Cargo.toml +++ b/crates/boringssl-sys/Cargo.toml @@ -11,6 +11,7 @@ bindgen = "0.69.1" [features] vendored-master = [] boring-2023-11-18 = [] +boring-2024-03-22 = [] sancov = [] asan = [] diff --git a/crates/boringssl-sys/build.rs b/crates/boringssl-sys/build.rs index 4e9efa8d2..4aa7eaefb 100644 --- a/crates/boringssl-sys/build.rs +++ b/crates/boringssl-sys/build.rs @@ -5,6 +5,8 @@ use boringssl_src::{build, BoringSSLOptions, GitRef}; fn main() { let git_ref: GitRef = if cfg!(feature = "boring-2023-11-18") { GitRef::Commit(String::from("698aa894c96412d4df20e2bb031d9eb9c9d5919a")) + } else if cfg!(feature = "boring-2024-03-22") { + GitRef::Commit(String::from("368d0d87d0bd00f8227f74ce18e8e4384eaf6afa")) } else if cfg!(feature = "vendored-master") { GitRef::Branch(String::from("master")) } else { diff --git a/tlspuffin/Cargo.toml b/tlspuffin/Cargo.toml index 6266c51df..523de4061 100644 --- a/tlspuffin/Cargo.toml +++ b/tlspuffin/Cargo.toml @@ -213,6 +213,19 @@ boringssl202311 = [ "deterministic", ] +boringssl202403 = [ + "boringssl-sys/boring-2024-03-22", + "tls12", + "tls13", + "boringssl-binding", + "tls12-session-resumption", + "tls13-session-resumption", + "transcript-extraction", + "claims", + "client-authentication-transcript-extraction", + "deterministic", +] + # Logs each execution of __sanitizer_cov_trace_pc_guard* sancov_pcguard_log = ["puffin/sancov_pcguard_log"] From 7d644128bf66b1daf304e577ba789acd8b61e7a7 Mon Sep 17 00:00:00 2001 From: Tom Gouville Date: Fri, 22 Mar 2024 10:52:43 +0100 Subject: [PATCH 41/63] add boringssl to gitlab CI --- .github/workflows/on_main_push.yml | 8 ++++++++ .github/workflows/on_pull_request.yml | 8 ++++++++ 2 files changed, 16 insertions(+) diff --git a/.github/workflows/on_main_push.yml b/.github/workflows/on_main_push.yml index 8f7dd43fe..a03df7aad 100644 --- a/.github/workflows/on_main_push.yml +++ b/.github/workflows/on_main_push.yml @@ -42,6 +42,8 @@ jobs: - wolfssl540-heap - wolfssl540_asan-perf - wolfssl540-perf + - boringssl202403 + - boringssl202403_asan - libssh - tlspuffin_no_default_features - sshpuffin_no_default_features @@ -115,6 +117,12 @@ jobs: - name: wolfssl540-perf crate: tlspuffin features: wolfssl540,fix-CVE-2022-39173,fix-CVE-2022-42905 + - name: boringssl202403 + crate: tlspuffin + features: boringssl202403 + - name: boringssl202403_asan + crate: tlspuffin + features: boringssl202403,asan - name: libssh crate: sshpuffin features: "" diff --git a/.github/workflows/on_pull_request.yml b/.github/workflows/on_pull_request.yml index d1ea173e0..d6079941d 100644 --- a/.github/workflows/on_pull_request.yml +++ b/.github/workflows/on_pull_request.yml @@ -30,6 +30,8 @@ jobs: - wolfssl530 - wolfssl530_asan - wolfssl540 + - boringssl202403 + - boringssl202403_asan - libssh include: - name: openssl111 @@ -61,6 +63,12 @@ jobs: - name: wolfssl530_asan crate: tlspuffin features: wolfssl530,asan + - name: boringssl202403 + crate: tlspuffin + features: boringssl202403 + - name: boringssl202403_asan + crate: tlspuffin + features: boringssl202403,asan - name: wolfssl540 crate: tlspuffin features: wolfssl540 From 39ee8c810913932f559fadda7ff178c7ac7cf6db Mon Sep 17 00:00:00 2001 From: Tom Gouville Date: Fri, 22 Mar 2024 17:40:01 +0100 Subject: [PATCH 42/63] removing useless gitignore rule --- .gitignore | 2 -- 1 file changed, 2 deletions(-) diff --git a/.gitignore b/.gitignore index c344d79b0..d1df18b24 100644 --- a/.gitignore +++ b/.gitignore @@ -38,5 +38,3 @@ coverage.csv *.html *.css stats.json.should-be-empty.log - -/crates/boringssl-src/boringssl/ From fac9f77078713eba1bb491203da1b5aa63aa6978 Mon Sep 17 00:00:00 2001 From: Tom Gouville Date: Mon, 25 Mar 2024 10:59:37 +0100 Subject: [PATCH 43/63] prevent building multiple PUTs at the same time --- tlspuffin/build.rs | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/tlspuffin/build.rs b/tlspuffin/build.rs index ac25786ef..2006cfc13 100644 --- a/tlspuffin/build.rs +++ b/tlspuffin/build.rs @@ -1,9 +1,14 @@ use std::process::Command; -#[cfg(all(feature = "openssl-binding", feature = "wolfssl-binding"))] -compile_error!("Selecting multiple vendored PUT is currently not supported: openssl/libressl and wolfssl feature flags are mutually exclusive."); - fn main() { + if cfg!(feature = "openssl-bindging") as u8 + + cfg!(feature = "wolfssl-bindging") as u8 + + cfg!(feature = "boringssl-bindging") as u8 + > 1 + { + compile_error!("Selecting multiple vendored PUT is currently not supported: openssl/libressl, wolfssl and boringssl feature flags are mutually exclusive."); + } + if cfg!(feature = "asan") { // NOTE adding compiler-rt to rpath for libasan is not straightforward // From 079ade63ba243827e3e6f1774368b91d8caeae3a Mon Sep 17 00:00:00 2001 From: Tom Gouville Date: Mon, 25 Mar 2024 11:03:32 +0100 Subject: [PATCH 44/63] adding licences for boringssl related crates --- THIRD_PARTY | 467 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 467 insertions(+) diff --git a/THIRD_PARTY b/THIRD_PARTY index 9e26b32b8..03ce48134 100644 --- a/THIRD_PARTY +++ b/THIRD_PARTY @@ -687,3 +687,470 @@ Email: licensing@wolfssl.com Phone: +1 425 245-8247 More information can be found on the wolfSSL website at www.wolfssl.com. + + +# boringssl + +Copyright 2011-2017 Google Inc. + 2013 Jack Lloyd + 2013-2014 Steven Fackler + 2020 Ivan Nikulin + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + + + +## APACHE 2.0 Licence + + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +# crates/boringssl-sys/src/lib.rs + +crates/boringssl-sys/src/lib.rs is derived from https://github.com/cloudflare/boring/blob/cdb76dcba2cd0f36bc9ba94c1a3b23137a8bbfe5/boring-sys/src/lib.rs +which is dual licensed under the terms of both the Apache License, Version 2.0 and +the MIT license without any additional terms or conditions. + +## MIT Licence + +Copyright (c) 2014 Alex Crichton +Copyright (c) 2020 Ivan Nikulin + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. + +## APACHE 2.0 Licence + + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. From 3d50daaa47802160669d49f1f8255c7c19deabce Mon Sep 17 00:00:00 2001 From: Tom Gouville Date: Mon, 25 Mar 2024 11:38:51 +0100 Subject: [PATCH 45/63] fix compile_error being evaluated all the time and crashing the build --- tlspuffin/build.rs | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/tlspuffin/build.rs b/tlspuffin/build.rs index 2006cfc13..64abcbcd3 100644 --- a/tlspuffin/build.rs +++ b/tlspuffin/build.rs @@ -1,14 +1,13 @@ use std::process::Command; -fn main() { - if cfg!(feature = "openssl-bindging") as u8 - + cfg!(feature = "wolfssl-bindging") as u8 - + cfg!(feature = "boringssl-bindging") as u8 - > 1 - { - compile_error!("Selecting multiple vendored PUT is currently not supported: openssl/libressl, wolfssl and boringssl feature flags are mutually exclusive."); - } +#[cfg(any( + all(feature = "openssl-binding", feature = "wolfssl-binding"), + all(feature = "openssl-binding", feature = "boringssl-binding"), + all(feature = "wolfssl-binding", feature = "boringssl-binding") +))] +compile_error!("Selecting multiple vendored PUT is currently not supported: openssl/libressl, wolfssl and boringssl feature flags are mutually exclusive."); +fn main() { if cfg!(feature = "asan") { // NOTE adding compiler-rt to rpath for libasan is not straightforward // From 8407c491c89b17a06f9fcb69f72717b403cd4a5a Mon Sep 17 00:00:00 2001 From: Tom Gouville Date: Mon, 25 Mar 2024 13:02:42 +0100 Subject: [PATCH 46/63] removing lto in debug mode --- Cargo.toml | 1 - 1 file changed, 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index f18338f61..43abe62e4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -51,4 +51,3 @@ debug = true [profile.dev] panic = "abort" -lto = true From d650c0dd4210392f71a70e4245d01fbb02c6c963 Mon Sep 17 00:00:00 2001 From: Tom Gouville Date: Mon, 25 Mar 2024 13:05:32 +0100 Subject: [PATCH 47/63] updating author list in Cargo.toml --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 43abe62e4..ecf1f554c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -25,7 +25,7 @@ exclude = [ [workspace.package] version = "0.1.0" edition = "2021" -authors = ["Maximilian Ammann ", "Lucca Hirschi ", "Michael Mera "] +authors = ["Maximilian Ammann ", "Lucca Hirschi ", "Michael Mera ", "Tom Gouville "] repository = "https://github.com/tlspuffin/tlspuffin" homepage = "https://github.com/tlspuffin/tlspuffin" license = "MIT OR Apache-2.0" From 8865f8b83db41dc5cf9ac7c220f2711d0b01e8ef Mon Sep 17 00:00:00 2001 From: Tom Gouville Date: Tue, 26 Mar 2024 16:15:07 +0100 Subject: [PATCH 48/63] fix boringssl build with gcov --- Cargo.lock | 1 + crates/boringssl-sys/Cargo.toml | 3 +++ crates/boringssl-sys/build.rs | 17 +++++++++++++++++ tlspuffin/Cargo.toml | 2 +- 4 files changed, 22 insertions(+), 1 deletion(-) diff --git a/Cargo.lock b/Cargo.lock index 64ec2ebc3..a9ff793bd 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -236,6 +236,7 @@ version = "0.1.0" dependencies = [ "bindgen", "boringssl-src", + "libc", ] [[package]] diff --git a/crates/boringssl-sys/Cargo.toml b/crates/boringssl-sys/Cargo.toml index 4ec5cbab2..7539486cb 100644 --- a/crates/boringssl-sys/Cargo.toml +++ b/crates/boringssl-sys/Cargo.toml @@ -4,6 +4,9 @@ version = "0.1.0" edition = "2021" links = "boring" +[dependencies] +libc = "0.2" + [build-dependencies] boringssl-src = { path = "../boringssl-src" } bindgen = "0.69.1" diff --git a/crates/boringssl-sys/build.rs b/crates/boringssl-sys/build.rs index 4aa7eaefb..332b0f80c 100644 --- a/crates/boringssl-sys/build.rs +++ b/crates/boringssl-sys/build.rs @@ -36,11 +36,28 @@ fn main() { println!("cargo:rustc-link-lib=static=ssl"); println!("cargo:rustc-link-lib=stdc++"); + if cfg!(feature = "gcov_analysis") { + let clang_output = std::process::Command::new("clang") + .args(["--print-resource-dir"]) + .output() + .expect("failed to use clang to get resource dir"); + let clang_resource_dir: &str = std::str::from_utf8(&clang_output.stdout).unwrap().trim(); + println!("cargo:rustc-link-search={}/lib/linux/", clang_resource_dir); + println!("cargo:rustc-link-lib=static=clang_rt.profile-x86_64"); + } + let include_path = out_dir.join("include"); let bindings_out_dir = PathBuf::from(env::var("OUT_DIR").unwrap()); let mut builder = bindgen::Builder::default() + .ctypes_prefix("::libc") + .raw_line("use libc::*;") + .allowlist_file(".*/openssl/[^/]+\\.h") + .allowlist_recursively(false) + .blocklist_function("BIO_vprintf") + .blocklist_function("BIO_vsnprintf") + .blocklist_function("OPENSSL_vasprintf") .derive_copy(true) .derive_debug(true) .derive_default(true) diff --git a/tlspuffin/Cargo.toml b/tlspuffin/Cargo.toml index 523de4061..cd3d58d09 100644 --- a/tlspuffin/Cargo.toml +++ b/tlspuffin/Cargo.toml @@ -235,7 +235,7 @@ sancov_libafl = ["puffin/sancov_libafl", "openssl-src?/sancov", "wolfssl-sys?/sa # Enables ASAN asan = ["openssl-src?/asan", "wolfssl-sys?/asan", "boringssl-sys?/asan"] -gcov_analysis = ["openssl-src?/gcov_analysis", "wolfssl-sys?/gcov_analysis", "wolfssl-sys?/gcov_analysis"] +gcov_analysis = ["openssl-src?/gcov_analysis", "wolfssl-sys?/gcov_analysis", "boringssl-sys?/gcov_analysis"] llvm_cov_analysis = ["openssl-src?/llvm_cov_analysis", "wolfssl-sys?/llvm_cov_analysis", "boringssl-sys?/llvm_cov_analysis"] # WolfSSL bindings are used From 08c0953feaeaa86781cb263ced2b3a781e646afd Mon Sep 17 00:00:00 2001 From: Tom Gouville Date: Thu, 28 Mar 2024 11:28:15 +0100 Subject: [PATCH 49/63] removing useless file --- crates/boringssl-src/wrapper.h | 3 --- 1 file changed, 3 deletions(-) delete mode 100644 crates/boringssl-src/wrapper.h diff --git a/crates/boringssl-src/wrapper.h b/crates/boringssl-src/wrapper.h deleted file mode 100644 index 08dcfda3b..000000000 --- a/crates/boringssl-src/wrapper.h +++ /dev/null @@ -1,3 +0,0 @@ -#include -#include -#include From 83186f414d6c818c667cfef990dd77d468320c96 Mon Sep 17 00:00:00 2001 From: Tom Gouville Date: Thu, 28 Mar 2024 12:56:23 +0100 Subject: [PATCH 50/63] documenting boringssl build flags and allow building with ASM --- crates/boringssl-src/src/lib.rs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/crates/boringssl-src/src/lib.rs b/crates/boringssl-src/src/lib.rs index 938aadfe3..bde5b2a0a 100644 --- a/crates/boringssl-src/src/lib.rs +++ b/crates/boringssl-src/src/lib.rs @@ -103,10 +103,13 @@ fn build_boringssl>(dest: &P, options: &BoringSSLOptions) -> Path .cflag("-g") .cxxflag("-g") .define("CMAKE_BUILD_TYPE", "Release") - .define("OPENSSL_NO_BUF_FREELISTS", "1") - .define("OPENSSL_NO_ASM", "1"); + .define("OPENSSL_NO_BUF_FREELISTS", "1"); if options.deterministic { + // FUZZ flag will enable deterministic mode in BoringSSL along with + // disabling all encryption. To prevent BoringSSL from disabling encryption + // we also pass the NO_FUZZER_MODE flag + // See https://github.com/google/boringssl/blob/master/FUZZING.md for details boring_conf.define("FUZZ", "1"); boring_conf.define("NO_FUZZER_MODE", "1"); } From 3239c2834c4d43c08617e694e77965a3d5919527 Mon Sep 17 00:00:00 2001 From: Tom Gouville Date: Thu, 28 Mar 2024 13:01:01 +0100 Subject: [PATCH 51/63] setting lto to true for debug builds to fix puffin hanging while fuzzing BoringSSL --- Cargo.toml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Cargo.toml b/Cargo.toml index ecf1f554c..76854c0d8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -51,3 +51,7 @@ debug = true [profile.dev] panic = "abort" +# TODO: Without LTO, the fuzzer hangs in debug releases with BoringSSL +# To reproduce the bug, simply disable lto and run +# `cargo run --bin tlspuffin --features boringssl202403` +lto = true From 8f147ab09866ddcaedbaeb493f1e0dad88ef8926 Mon Sep 17 00:00:00 2001 From: Tom Gouville Date: Thu, 28 Mar 2024 13:53:45 +0100 Subject: [PATCH 52/63] dropping useless test files --- crates/boring/test/alt_name_cert.pem | 22 ----------- crates/boring/test/cert.pem | 19 ---------- crates/boring/test/certs.pem | 40 -------------------- crates/boring/test/cms.p12 | Bin 1709 -> 0 bytes crates/boring/test/cms_pubkey.der | Bin 688 -> 0 bytes crates/boring/test/dhparams.pem | 8 ---- crates/boring/test/dsa.pem | 12 ------ crates/boring/test/dsa.pem.pub | 12 ------ crates/boring/test/dsaparam.pem | 9 ----- crates/boring/test/identity.p12 | Bin 3386 -> 0 bytes crates/boring/test/key.der | Bin 1193 -> 0 bytes crates/boring/test/key.der.pub | Bin 294 -> 0 bytes crates/boring/test/key.pem | 28 -------------- crates/boring/test/key.pem.pub | 9 ----- crates/boring/test/keystore-empty-chain.p12 | Bin 2514 -> 0 bytes crates/boring/test/nid_test_cert.pem | 12 ------ crates/boring/test/nid_uid_test_cert.pem | 24 ------------ crates/boring/test/pkcs1.pem.pub | 8 ---- crates/boring/test/pkcs8-nocrypt.der | Bin 1216 -> 0 bytes crates/boring/test/pkcs8.der | Bin 1298 -> 0 bytes crates/boring/test/root-ca.key | 27 ------------- crates/boring/test/root-ca.pem | 21 ---------- crates/boring/test/rsa-encrypted.pem | 30 --------------- crates/boring/test/rsa.pem | 27 ------------- crates/boring/test/rsa.pem.pub | 9 ----- 25 files changed, 317 deletions(-) delete mode 100644 crates/boring/test/alt_name_cert.pem delete mode 100644 crates/boring/test/cert.pem delete mode 100644 crates/boring/test/certs.pem delete mode 100644 crates/boring/test/cms.p12 delete mode 100644 crates/boring/test/cms_pubkey.der delete mode 100644 crates/boring/test/dhparams.pem delete mode 100644 crates/boring/test/dsa.pem delete mode 100644 crates/boring/test/dsa.pem.pub delete mode 100644 crates/boring/test/dsaparam.pem delete mode 100644 crates/boring/test/identity.p12 delete mode 100644 crates/boring/test/key.der delete mode 100644 crates/boring/test/key.der.pub delete mode 100644 crates/boring/test/key.pem delete mode 100644 crates/boring/test/key.pem.pub delete mode 100644 crates/boring/test/keystore-empty-chain.p12 delete mode 100644 crates/boring/test/nid_test_cert.pem delete mode 100644 crates/boring/test/nid_uid_test_cert.pem delete mode 100644 crates/boring/test/pkcs1.pem.pub delete mode 100644 crates/boring/test/pkcs8-nocrypt.der delete mode 100644 crates/boring/test/pkcs8.der delete mode 100644 crates/boring/test/root-ca.key delete mode 100644 crates/boring/test/root-ca.pem delete mode 100644 crates/boring/test/rsa-encrypted.pem delete mode 100644 crates/boring/test/rsa.pem delete mode 100644 crates/boring/test/rsa.pem.pub diff --git a/crates/boring/test/alt_name_cert.pem b/crates/boring/test/alt_name_cert.pem deleted file mode 100644 index d9e9f90e1..000000000 --- a/crates/boring/test/alt_name_cert.pem +++ /dev/null @@ -1,22 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIDsDCCApigAwIBAgIBATANBgkqhkiG9w0BAQsFADBFMQswCQYDVQQGEwJBVTET -MBEGA1UECAwKU29tZS1TdGF0ZTEhMB8GA1UECgwYSW50ZXJuZXQgV2lkZ2l0cyBQ -dHkgTHRkMB4XDTE4MDExNTExMDcwM1oXDTI4MDExMzExMDcwM1owfDELMAkGA1UE -BhMCVVMxCzAJBgNVBAgMAk5ZMREwDwYDVQQHDAhOZXcgWW9yazEVMBMGA1UECgwM -RXhhbXBsZSwgTExDMTYwNAYDVQQDDC1FeGFtcGxlIENvbXBhbnkvZW1haWxBZGRy -ZXNzPXRlc3RAZXhhbXBsZS5jb20wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEK -AoIBAQCo9CWMRLMXo1CF/iORh9B4NhtJF/8tR9PlG95sNvyWuQQ/8jfev+8zErpl -xfLkt0pJqcoiZG8g9NU0kU6o5T+/1QgZclCAoZaS0Jqxmoo2Yk/1Qsj16pnMBc10 -uSDk6V9aJSX1vKwONVNSwiHA1MhX+i7Wf7/K0niq+k7hOkhleFkWgZtUq41gXh1V -fOugka7UktYnk9mrBbAMjmaloZNn2pMMAQxVg4ThiLm3zvuWqvXASWzUZc7IAd1G -bN4AtDuhs252eqE9E4iTHk7F14wAS1JWqv666hReGHrmZJGx0xQTM9vPD1HN5t2U -3KTfhO/mTlAUWVyg9tCtOzboKgs1AgMBAAGjdDByMAkGA1UdEwQCMAAwCwYDVR0P -BAQDAgWgMFgGA1UdEQRRME+CC2V4YW1wbGUuY29thwR/AAABhxAAAAAAAAAAAAAA -AAAAAAABgRB0ZXN0QGV4YW1wbGUuY29thhZodHRwOi8vd3d3LmV4YW1wbGUuY29t -MA0GCSqGSIb3DQEBCwUAA4IBAQAx14G99z/MnSbs8h5jSos+dgLvhc2IQB/3CChE -hPyELc7iyw1iteRs7bS1m2NZx6gv6TZ6VydDrK1dnWSatQ7sskXTO+zfC6qjMwXl -IV+u7T8EREwciniIA82d8GWs60BGyBL3zp2iUOr5ULG4+c/S6OLdlyJv+fDKv+Xo -fKv1UGDi5rcvUBikeNkpEPTN9UsE9/A8XJfDyq+4RKuDW19EtzOOeVx4xpHOMnAy -VVAQVMKJzhoXtLF4k2j409na+f6FIcZSBet+plmzfB+WZNIgUUi/7MQIXOFQRkj4 -zH3SnsPm/IYpJzlH2vHhlqIBdaSoTWpGVWPq7D+H8OS3mmXF ------END CERTIFICATE----- diff --git a/crates/boring/test/cert.pem b/crates/boring/test/cert.pem deleted file mode 100644 index 032fe60e9..000000000 --- a/crates/boring/test/cert.pem +++ /dev/null @@ -1,19 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIDGzCCAgMCCQCHcfe97pgvpTANBgkqhkiG9w0BAQsFADBFMQswCQYDVQQGEwJB -VTETMBEGA1UECAwKU29tZS1TdGF0ZTEhMB8GA1UECgwYSW50ZXJuZXQgV2lkZ2l0 -cyBQdHkgTHRkMB4XDTE2MDgxNDE3MDAwM1oXDTI2MDgxMjE3MDAwM1owWjELMAkG -A1UEBhMCQVUxEzARBgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoMGEludGVybmV0 -IFdpZGdpdHMgUHR5IEx0ZDETMBEGA1UEAwwKZm9vYmFyLmNvbTCCASIwDQYJKoZI -hvcNAQEBBQADggEPADCCAQoCggEBAKj0JYxEsxejUIX+I5GH0Hg2G0kX/y1H0+Ub -3mw2/Ja5BD/yN96/7zMSumXF8uS3SkmpyiJkbyD01TSRTqjlP7/VCBlyUIChlpLQ -mrGaijZiT/VCyPXqmcwFzXS5IOTpX1olJfW8rA41U1LCIcDUyFf6LtZ/v8rSeKr6 -TuE6SGV4WRaBm1SrjWBeHVV866CRrtSS1ieT2asFsAyOZqWhk2fakwwBDFWDhOGI -ubfO+5aq9cBJbNRlzsgB3UZs3gC0O6GzbnZ6oT0TiJMeTsXXjABLUlaq/rrqFF4Y -euZkkbHTFBMz288PUc3m3ZTcpN+E7+ZOUBRZXKD20K07NugqCzUCAwEAATANBgkq -hkiG9w0BAQsFAAOCAQEASvYHuIl5C0NHBELPpVHNuLbQsDQNKVj3a54+9q1JkiMM -6taEJYfw7K1Xjm4RoiFSHpQBh+PWZS3hToToL2Zx8JfMR5MuAirdPAy1Sia/J/qE -wQdJccqmvuLkLTSlsGbEJ/LUUgOAgrgHOZM5lUgIhCneA0/dWJ3PsN0zvn69/faY -oo1iiolWiIHWWBUSdr3jM2AJaVAsTmLh00cKaDNk37JB940xConBGSl98JPrNrf9 -dUAiT0iIBngDBdHnn/yTj+InVEFyZSKrNtiDSObFHxPcxGteHNrCPJdP1e+GqkHp -HJMRZVCQpSMzvHlofHSNgzWV1MX5h1CP4SGZdBDTfA== ------END CERTIFICATE----- diff --git a/crates/boring/test/certs.pem b/crates/boring/test/certs.pem deleted file mode 100644 index 9d3516620..000000000 --- a/crates/boring/test/certs.pem +++ /dev/null @@ -1,40 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIDGzCCAgMCCQCHcfe97pgvpTANBgkqhkiG9w0BAQsFADBFMQswCQYDVQQGEwJB -VTETMBEGA1UECAwKU29tZS1TdGF0ZTEhMB8GA1UECgwYSW50ZXJuZXQgV2lkZ2l0 -cyBQdHkgTHRkMB4XDTE2MDgxNDE3MDAwM1oXDTI2MDgxMjE3MDAwM1owWjELMAkG -A1UEBhMCQVUxEzARBgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoMGEludGVybmV0 -IFdpZGdpdHMgUHR5IEx0ZDETMBEGA1UEAwwKZm9vYmFyLmNvbTCCASIwDQYJKoZI -hvcNAQEBBQADggEPADCCAQoCggEBAKj0JYxEsxejUIX+I5GH0Hg2G0kX/y1H0+Ub -3mw2/Ja5BD/yN96/7zMSumXF8uS3SkmpyiJkbyD01TSRTqjlP7/VCBlyUIChlpLQ -mrGaijZiT/VCyPXqmcwFzXS5IOTpX1olJfW8rA41U1LCIcDUyFf6LtZ/v8rSeKr6 -TuE6SGV4WRaBm1SrjWBeHVV866CRrtSS1ieT2asFsAyOZqWhk2fakwwBDFWDhOGI -ubfO+5aq9cBJbNRlzsgB3UZs3gC0O6GzbnZ6oT0TiJMeTsXXjABLUlaq/rrqFF4Y -euZkkbHTFBMz288PUc3m3ZTcpN+E7+ZOUBRZXKD20K07NugqCzUCAwEAATANBgkq -hkiG9w0BAQsFAAOCAQEASvYHuIl5C0NHBELPpVHNuLbQsDQNKVj3a54+9q1JkiMM -6taEJYfw7K1Xjm4RoiFSHpQBh+PWZS3hToToL2Zx8JfMR5MuAirdPAy1Sia/J/qE -wQdJccqmvuLkLTSlsGbEJ/LUUgOAgrgHOZM5lUgIhCneA0/dWJ3PsN0zvn69/faY -oo1iiolWiIHWWBUSdr3jM2AJaVAsTmLh00cKaDNk37JB940xConBGSl98JPrNrf9 -dUAiT0iIBngDBdHnn/yTj+InVEFyZSKrNtiDSObFHxPcxGteHNrCPJdP1e+GqkHp -HJMRZVCQpSMzvHlofHSNgzWV1MX5h1CP4SGZdBDTfA== ------END CERTIFICATE----- ------BEGIN CERTIFICATE----- -MIIDXTCCAkWgAwIBAgIJAOIvDiVb18eVMA0GCSqGSIb3DQEBCwUAMEUxCzAJBgNV -BAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEwHwYDVQQKDBhJbnRlcm5ldCBX -aWRnaXRzIFB0eSBMdGQwHhcNMTYwODE0MTY1NjExWhcNMjYwODEyMTY1NjExWjBF -MQswCQYDVQQGEwJBVTETMBEGA1UECAwKU29tZS1TdGF0ZTEhMB8GA1UECgwYSW50 -ZXJuZXQgV2lkZ2l0cyBQdHkgTHRkMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB -CgKCAQEArVHWFn52Lbl1l59exduZntVSZyDYpzDND+S2LUcO6fRBWhV/1Kzox+2G -ZptbuMGmfI3iAnb0CFT4uC3kBkQQlXonGATSVyaFTFR+jq/lc0SP+9Bd7SBXieIV -eIXlY1TvlwIvj3Ntw9zX+scTA4SXxH6M0rKv9gTOub2vCMSHeF16X8DQr4XsZuQr -7Cp7j1I4aqOJyap5JTl5ijmG8cnu0n+8UcRlBzy99dLWJG0AfI3VRJdWpGTNVZ92 -aFff3RpK3F/WI2gp3qV1ynRAKuvmncGC3LDvYfcc2dgsc1N6Ffq8GIrkgRob6eBc -klDHp1d023Lwre+VaVDSo1//Y72UFwIDAQABo1AwTjAdBgNVHQ4EFgQUbNOlA6sN -XyzJjYqciKeId7g3/ZowHwYDVR0jBBgwFoAUbNOlA6sNXyzJjYqciKeId7g3/Zow -DAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEAVVaR5QWLZIRR4Dw6TSBn -BQiLpBSXN6oAxdDw6n4PtwW6CzydaA+creiK6LfwEsiifUfQe9f+T+TBSpdIYtMv -Z2H2tjlFX8VrjUFvPrvn5c28CuLI0foBgY8XGSkR2YMYzWw2jPEq3Th/KM5Catn3 -AFm3bGKWMtGPR4v+90chEN0jzaAmJYRrVUh9vea27bOCn31Nse6XXQPmSI6Gyncy -OAPUsvPClF3IjeL1tmBotWqSGn1cYxLo+Lwjk22A9h6vjcNQRyZF2VLVvtwYrNU3 -mwJ6GCLsLHpwW/yjyvn8iEltnJvByM/eeRnfXV6WDObyiZsE/n6DxIRJodQzFqy9 -GA== ------END CERTIFICATE----- diff --git a/crates/boring/test/cms.p12 b/crates/boring/test/cms.p12 deleted file mode 100644 index c4f96b6996e41e31fb19f982c59a8a0fad90462b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1709 zcmY+DdpHvcAIEpIHn+LVn7c`1ZjY9B3L8 zjw|#n{HOuk1r&0iF%S;)#10O_bHLQUBB+o9_T5255D>7l2!AC&yfIYuKOfYAP`oAt z=CNcSqgcI!0fAtEd=6NvkuSk#s^qLM4fYzT)`$021R*6ukUX`nrU1JEH}>)EcXsPU zg-pBTda9))RCK2vRGlO@C4X=}LG)fw)ak9vetvfTIiVe5fs44L(O_&^APyl<8H2um z()M+}wK5_tWSivIzB1+y{08i}oay!IU`&+gG~RO}#Y-U1Om0q}hW={fW^6h#v{~xx z8S5N`?hw^s`;}VGNFyH&Ugsls9JM($cUxB6`iY3xFnW-fXN%$rCE*tOXX*8mD@R7- zcgH-zWng89$T`JdgjDip{Gy zpX^#5_(y<8O4lQ&nNiSzxI1mJC1b93beM~-0;!)K43}<3}rqxg-&UbyI3c!nC!e%y2r)*oJKjT9i>8QHCsB0kjp(^Fd{UX%*W9lcE>ljVg@lT`mjduj) z*)8@MFr9zVW;5%?=IMUsi}gTWf+p=5t!`I8D51Hg?Vhu8;=8wTKMMrcbgI3KS~5=Z z#YF^~eM78DG>KZ1Mpa*29`^2g;|)YdM@;Trg(Y5uS13P5!}-2a&5^;b!>tR`iScVQ z-5+}jLH(8aXe+(#{f1S@qJ`z%*dyg+?e{paXhnR4Hh>0azO7O9FTkm&+p7ERPDbysRBVeVdD-I{mnA)7t1=+ z^B=lGy?9?N+d0oaU_0b)ax1tmaZ2e|nTyw{yclv)YX9A`y16cePjcf=)i)yUoME(% zGLgM%MVXVdm{7ydL1GpkeB6)M3iB|tKDyMhIFgZG$IY6?jXG+?Rc2UaxhLInU#r0( z`AKvD%1qQJ&Pz7nYX_A{*G>K0jKWqJgPfLvaXJn&r}hH8EFdxx1|)I2uWB@|Ell>O zdJaBu!JG;>RLkA6HJiB5G<=XW+}0Cv=<`h+fwZMNeu$-QuuA{n@0PA5dOw1=Q8rjj zQ9qwYW}%}ZW|AR1^SR4Bl7Cb&ys7EO>eGyy^VpYC0O}*LfnF)&e!z;U^GQI}Dwc!K z3#b*jYdfs!L?;K}LhWA8QGeBcqaB?`mA+-n%#J zl~ZoBX$$H+!}IhG8w!9;H96`CvrrqW)rt<$?Rgi=HHTkdI@V=u z>1Yq|sZ~AR*Uk16r)P(G62i=6>5VMYZvE~a4EG!KD%im{VY)2pbMm6ya}y82?Ua3p zhv$X{mvT=8zF%oa6cofy2*T#Dwfpn+Sdzj8Zt3+bvYS*~IAJ^n5-x_R0LMRo0 z*RJi%s2aTo%o-wA4;W4_x0Z;fbmW`+!GK1S1>L6m)4Q{(F>n55&vpxtNi7g*_sSz% z>RuhWweS)?H)7~#ATZgSWP1ep$hX(>7A<5s$3U~Cj)!5a;n+ijz$zd(576gUcP4Q3 z9QMrEL}XoK>)X4QWhHyrP1Ox9Ni0b<6g3bA33Bo95k8Ii+zvawsseGB7vx zG8i;=GBq|b91ixXOq=@5P_p*Z{*#&#i#~3ZJixg5*=_$P7iAJ;+nD`Uu_yjsdvVS6 zO}84OSf1#Ko%?t(ZPTQ4euL*)v6 z{2w!J@V~k2me-m_xvs!&(Fm?xT(i7({F*E3xc@@cq3p9KYfmdxGBGnUFfI-@2sDrd zhPf;six`WDZQI)&OWtkB6}P{#&E7M4+S3PZiU#r^X=N4(1F;6|3iv?^gc%wCv#=U4 z11V%b17n^U=vh`@iLz7ean;Xlr)T>0)rr*|(A&(qcs+CQvaBLCf%Yq+OH^_~nm+2N zzURr`6Y=@W+j-lRPJa+zHR+az%5>*XtSgpU`RXn6wf->mf0yT?-O+#E`UbVG@VG2+ tW?ACCKBeQVC4U&%Q=dw1?Y^TnvwffI2CFTpMrSMo9ruMg_=E&x0|0_v@QnZf diff --git a/crates/boring/test/dhparams.pem b/crates/boring/test/dhparams.pem deleted file mode 100644 index 6e4d4c68c..000000000 --- a/crates/boring/test/dhparams.pem +++ /dev/null @@ -1,8 +0,0 @@ ------BEGIN DH PARAMETERS----- -MIIBCAKCAQEAh3Betv+hf5jNsOmGXU8oxuABD2B8r0yU8FVgjnCZBSVo61qJ0A2d -J6r8rYKbjtolnrZN/V4IPSzYvxurHbu8nbiFVyhOySPchI2Fu+YT/HsSe/0MH9bW -gJTNzmutWoy9VxtWLCmXnOSZHep3MZ1ZNimno6Kh2qQ7VJr0+KF8GbxUKOPv4SqK -NBwouIQXFc0pE9kGhcGKbr7TnHhyJFCRLNP1OVDQZbcoKjk1Vh+5sy7vM2VUTQmM -yOToT2LEZVAUJXNumcYMki9MIwfYCwYZbNt0ZEolyHzUEesuyHfU1eJd6+sKEjUz -5GteQIR7AehxZIS+cytu7BXO7B0owLJ2awIBAg== ------END DH PARAMETERS----- diff --git a/crates/boring/test/dsa.pem b/crates/boring/test/dsa.pem deleted file mode 100644 index 9b5501848..000000000 --- a/crates/boring/test/dsa.pem +++ /dev/null @@ -1,12 +0,0 @@ ------BEGIN DSA PRIVATE KEY----- -MIIBuwIBAAKBgQCkKe/jtYKJNQafaE7kg2aaJOEPUV0Doi451jkXHp5UfLh6+t42 -eabSGkE9WBAlILgaB8yHckLe9+zozN39+SUDp94kb2r38/8w/9Ffhbsep9uiyOj2 -ZRQur6SkpKQDKcnAd6IMZXZcvdSgPC90A6qraYUZKq7Csjn63gbC+IvXHwIVAIgS -PE43lXD8/rGYxos4cxCgGGAxAoGASMV56WhLvVQtWMVI36WSIxbZnC2EsnNIKeVW -yXnP/OmPJ2mdezG7i1alcwsO2TnSLbvjvGPlyzIqZzHvWC8EmDqsfbU+n8we/Eal -sm5nloC8m9ECWpbTzbNdvrAAj9UPVWjcDwg7grAGGysh6lGbBv5P+4zL/niq1UiE -LnKcifgCgYEAo6mAasO0+MVcu8shxxUXXNeTLsZ8NB/BIx9EZ/dzE23ivNW8dq1A -eecAAYhssI2m/CspQvyKw+seCvg4FccxJgB3+mGOe+blFHwO3eAwoyRn/t3DZDHh -FjxKKRsQdy4BkZv+vhTyIYYCw0iPZ5Wfln+pyGGTveIDED1MPG+J6c8CFCJAUlEl -4nHvbC15xLXXpd46zycY ------END DSA PRIVATE KEY----- diff --git a/crates/boring/test/dsa.pem.pub b/crates/boring/test/dsa.pem.pub deleted file mode 100644 index ae298d093..000000000 --- a/crates/boring/test/dsa.pem.pub +++ /dev/null @@ -1,12 +0,0 @@ ------BEGIN PUBLIC KEY----- -MIIBtzCCASsGByqGSM44BAEwggEeAoGBAKQp7+O1gok1Bp9oTuSDZpok4Q9RXQOi -LjnWORcenlR8uHr63jZ5ptIaQT1YECUguBoHzIdyQt737OjM3f35JQOn3iRvavfz -/zD/0V+Fux6n26LI6PZlFC6vpKSkpAMpycB3ogxldly91KA8L3QDqqtphRkqrsKy -OfreBsL4i9cfAhUAiBI8TjeVcPz+sZjGizhzEKAYYDECgYBIxXnpaEu9VC1YxUjf -pZIjFtmcLYSyc0gp5VbJec/86Y8naZ17MbuLVqVzCw7ZOdItu+O8Y+XLMipnMe9Y -LwSYOqx9tT6fzB78RqWybmeWgLyb0QJaltPNs12+sACP1Q9VaNwPCDuCsAYbKyHq -UZsG/k/7jMv+eKrVSIQucpyJ+AOBhQACgYEAo6mAasO0+MVcu8shxxUXXNeTLsZ8 -NB/BIx9EZ/dzE23ivNW8dq1AeecAAYhssI2m/CspQvyKw+seCvg4FccxJgB3+mGO -e+blFHwO3eAwoyRn/t3DZDHhFjxKKRsQdy4BkZv+vhTyIYYCw0iPZ5Wfln+pyGGT -veIDED1MPG+J6c8= ------END PUBLIC KEY----- diff --git a/crates/boring/test/dsaparam.pem b/crates/boring/test/dsaparam.pem deleted file mode 100644 index 53bda543b..000000000 --- a/crates/boring/test/dsaparam.pem +++ /dev/null @@ -1,9 +0,0 @@ ------BEGIN DSA PARAMETERS----- -MIIBHgKBgQCkKe/jtYKJNQafaE7kg2aaJOEPUV0Doi451jkXHp5UfLh6+t42eabS -GkE9WBAlILgaB8yHckLe9+zozN39+SUDp94kb2r38/8w/9Ffhbsep9uiyOj2ZRQu -r6SkpKQDKcnAd6IMZXZcvdSgPC90A6qraYUZKq7Csjn63gbC+IvXHwIVAIgSPE43 -lXD8/rGYxos4cxCgGGAxAoGASMV56WhLvVQtWMVI36WSIxbZnC2EsnNIKeVWyXnP -/OmPJ2mdezG7i1alcwsO2TnSLbvjvGPlyzIqZzHvWC8EmDqsfbU+n8we/Ealsm5n -loC8m9ECWpbTzbNdvrAAj9UPVWjcDwg7grAGGysh6lGbBv5P+4zL/niq1UiELnKc -ifg= ------END DSA PARAMETERS----- diff --git a/crates/boring/test/identity.p12 b/crates/boring/test/identity.p12 deleted file mode 100644 index d16abb8c70627bd959667495e578fb6f010a7f1d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3386 zcmY+Fc{CIb*T-kZ%-F^$D#rP(jjrM6DXq;ULSwJ0;C7#;$Z7w z9PHO$c@c@DfAsGWG#5wj^;dEO0RexD!@nm$qyUuZe?P#0P$Vmufe?7XI$4!A4gxU% zusC`o{$ASWtHZ!%6&Yr};{6u`2KLv+>Oc-hrRqJ_6t51LvyFjb*MY2v@UpI}BEGHo zvK<0`n{R9EEh2758@vOSP&dDqOy0%>n)6OY2s1K@{ZZ4dUib$1=yGu{qy4)68IXNX zkk#_0j(a5M*dt*Sb*_j1P%B2^j47C!ky#GS(OVUXx=1c`4$i|hrFN!o2AI7yk$-hcE5vQ_4v$r`H0%4V>8 z@eO82W_dR!EPHOKwd{tqnGp6|1N4%pQ^bZ&wmM^1xKK`mmp5~Z!-*jOxzzTtyjI@N znR!#3*mi1n<$H_gljnHMOm{8 zY+OW$1dI#%nC00mv=w|&H?Ds_&Q&>zV$~?as>se&LcGBkC+KuH))CEF+bqRg55Y-3 z?~vHFBT!PxQJ=a_tK7s`O43jI#enGN$Sir<+d_5iA)Nn{pocY~r%FKRo?P!z?Yz^^ltS`1rG&W|$}6 zIK1R;ZV;$x`_4!Z3O$S$y|fdG=LX_+wEsUm|gb|J|R6=$2&5w5;*9HoUf+ zLC&xB@i{S7wciFKq`HokLs&MYUDh;a#&Nq`>{RQv)JdX8iEVvb{5|aGL9i^^AOpXY z&L6LF6u+99$t7+kFys94TO$8LR-yenXgMarH6$3GlTQl|GYr#AJ*J8<*w1~fu~s1O zdhUXpvVUHaLYgm_R_B=VPu;^q1k37|S#Az*#kI@%`yAGFcpg*|9J$55Dn2i3qil7T z#{o*c=&(?Um6=WCnJ<;C^yU0Wn5+U;LCTeF5LRZG`&7h5u>K>DCz^!_kAQ+Uk_>p^ZJ-AG#sMX^}swaF9O348d8KL2>E z)<=Onr|{6URV7{c-I=%8nti`|U(Z3YP74t0c_K)3>xS6vW%e^B29#WNrqLHAUIEy{ zzB?q&G}1_h*%0C4?15@moi|7#SL*3DFn*TcNo)PGTXh0X35{``dR4uHIDDveSU-f9 z{K;uqKl-Nv+V4mA(kBrDEVii_A1b!KJS}U4Iyn5L=6`(p(ctU_de;X1DiO>4u2__k zVlWt+97!S9u=F;5c}*O*ufnS{X8;m3K*h#T0PXmn;cX%gWvELfIb5GGobB|9yhDDpE&NP`u@Gnp&0 zQZLqLDBx6TpRdU#?(psb%LU%s+uHH3^q4!9CoL-jgzIVo56|~LFj*Dya8o&rW&R9f z#MCF&8DNgw4~2m6o|%0c4_>q*LnYa8v7A*R-OG2mtCse5WW>N06;mbHTJR0ly~=vb zjkL_*lL_>a|0oF63*HEu&>67XeZie|`a^`89-;zuIMvx0wZDKHFrH{;{>A+ay$ZLY zQ&)*|(POvl>U&=z{Tg-ooGiyts!|CuF0TS^0VgtL{JJ0I`v@^G`!EHbzFi}2 zL*x~uu?J$3fnQa7=3r+w1G;jan4eqgmvr-O)nlM%Da;H8qk?wE67l%lcqMyzw_r@o02)63je6Xu1-n29!*PLHn-90_SxMGSU5yp=aUNgAPf&JU@M3Mr*>n3*Qfs4@UZdr)3<49u!j(8;{>P^eE^Osav(h>l?db zz-D`eK9#oSSADv+c=1$Wl1_5pV%p++fJluaNC@?&3cRf~mUihw;wH~1NpD-OAhEp@ zp%J>UkR&rBuE;-O(>>(RI-$%WT}_LVD?y;mD%UYD&1i4MAzBQ}1OKO=ov1 zqOb6W(VoB8*XL$(xwOFE$}X1Q4kfSMF15mhjE{%TXEK8mEZLjGl#c@Kdami3&$?8R z3X0QOt>y=BbRV!lHr4bxPL(_bvynK6$A17e7YDHh;~?gLW%S=PgfjmhZkd3fTpV}< zi36|x7lrBmq3~Tpke%T}RQEp=2IIiJs)bH-t;&|}Fh+NaTeU~I+z6U$_st%d4& z-GhEwwpii2MIE&MtZ#2+NiaN#V8qoDMFa~9WuL=dYyA0VF!^rHt`>`@lvQaI9rjF% zf>&A5%p>?lyx=A)lifEe-ZTF_&hx;HqdgC=j^D`xrXHl2F1=@bAN8)917)sJ6!*me z<7HAMfY#b&Kyj?9V{7jX0ex*Do!y1*jQ4BlZqzj@WpzG)?+b1vXK$aw@ws>CE|ZxA z5+_}aqAICBLaX0@Sx&A-B{8F6=5ieD+o4bn7mz)?DFYwS9ISfcSkc1D?^7F(MYzNFqebD~<*>|~&C#$*Y)t41%< zSUb^f@Dm)@x`dT(>NzTU>>Qbs5c=rJ4Yz`@*9~DI<0a2AHA?iN*a+E!i8J>{0~PG?gk>1>Zj*jW^JtpfIAm$MRWkXoi1Cv@Ar>b zH0;ah2{C4g3|27>74O!U!X+684ZU@I&-k@Ud2^qCUNAL{TZwyyN$G#HNDrFsP#65c zrxf~mk;jg7wV|ONPtWwMdR6?%)Ot`$mI?|@QaMOVx=?!cXeO8~Fz9+qIZ5WmPKHb7 zgofgUUSjO+ua`8j{?2gr-`{@-43inOgBoKao68B6iAOcPQPh6Q%T2XayOCFbynb>X zH$308=bjoRmehcbbLNXtZBq z0J~mAB-w(EFUlR4pRu`v?GEVIWM&zLpe{CcX2(DI&Sy^R84J751Y9rEzC(o6CO0>y z=E{ugtGsQie~J(y5De$j6$PK5iB=^dz9SE8W5!8={%vccv}UmILPt41Z;;(tc^)R{ zZ=k`tzDp0hSkCT9WhKp4h)^M!%VaxWo6p-gp4Xnu=HqXHv&7k?hh-$IK1 zzdJH=%5w>F0z3c@{)P*{8Q=$y1-Sn0@Bil#LY@oEY0E8>a&AS}wv{Gv=((Kjlgzys zg_K7MBN?F(X;wNA9|QpA+;x-8N-+3rFwTsLlk6hIQoAssV1}Dt>u@5F2fhDT>Hh%N CKSRy{ diff --git a/crates/boring/test/key.der b/crates/boring/test/key.der deleted file mode 100644 index 6b6209fd1fbe809a335cf29b263d9ffd5d1b9da0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1193 zcmV;a1XlYnf&`@k0RRGm0RaH0^d*c$vlpXKh5jRvhtPO78%Y=cEl1Pk8{TX-{Fb={ zKk_%;zwa{=x@E=k(3-KDiZ)_T^+L$? z>Y2<1&2+gSWzNU}-9~KQ z0JJ-yvu<{Jp*<6blO9gR*Ngy5QdX+|y6O~O7<%Spk+IVh6EoY-4^hqL-IUy<--Pex zPEZtCT%h*Qtvfd8Dho9N0|5X50)hbn0HWjQgNX#C=#|Xr&A$|rAR_`nfqF|^vE_8` z2c;+Xic=e9axd|18_NU|`$ffBzVJy-gC)6avm8%*V7+ z1_t59C&GpC32iN`4t(E-NYO%S826Zkzq)RE-9;wc-Dij33EfeuV78P~IUh%vwBN$d zUuB8qY|+1c=rJpFKxztASvyT0VYFz>Ale5sRNp4S{nINLoMDnh=E1$>Sj%&We`-04 zD6-p!AX~h6p-Yy6u@-u^M}Wq};R1ny0NAAnWQ2}*Vp0Xd$s`2|+6&-Z^Jw64)*}Oe z_ZN-bl4Y`U<$d+gHhH?YTP@XHpnS=_BWd)T@pQ{zf1mZnNv|7%7taIPbaQBV@>vVq zlnF=rT5HUlPd}Js`tzJjjqL`sMzU8@_HXu73mwh=6n7FH11b$&pV_g%67N_VEdqgo z0LP_7AoFjmabS**yghHl8Mb3mflu0`YEQeF@TZ_gdtw zEc(=g$haaDlImCWw1;_$@)Tfl=wXrA&5!v9SK+2j=d}R*Us=Y3ml_81q}yMRZ0lo68qD3r%wQ}z zCh$?VD_8lNJ;)IZEa5cszT}I=-Vin!#_nK~-yQfqD9Qx}WJE1n9G8VxsXpFnHLng~ z0)c@5$3<8fBcg?ttnc3gA>8||Z7*>D~Kv+I$4zvy%An0)c@5lTMfFGT$Uqdt4*iAJD?I-!9|> zzMbamG^Li-7cR;pil@zMtqV5(6vNh9$_2EtOFf*w{_oidMMYJv9np`#NYn@sB~M&Z zz?Rudqk|Hf$ThqmY;O335u@G`JIt;LBKLY&wcpw~C?sGlQ0ondAf&n5h4F(A+hDe6@4FLfG1potr0S^E$f&mHwf&l>luU6MGn_40Ri}dwN z@vhb=Z^HB98^rxJ@#6tOl=Mn-(3yI4L%sqMDBLwcAdMQ8ond&)XdY^&MKU(RSH~_s@BghpE?S-IiLB>jW0gZG_YR$c2X>vw0s{d60bXc_hyVZp diff --git a/crates/boring/test/key.pem b/crates/boring/test/key.pem deleted file mode 100644 index d381795d3..000000000 --- a/crates/boring/test/key.pem +++ /dev/null @@ -1,28 +0,0 @@ ------BEGIN PRIVATE KEY----- -MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQCo9CWMRLMXo1CF -/iORh9B4NhtJF/8tR9PlG95sNvyWuQQ/8jfev+8zErplxfLkt0pJqcoiZG8g9NU0 -kU6o5T+/1QgZclCAoZaS0Jqxmoo2Yk/1Qsj16pnMBc10uSDk6V9aJSX1vKwONVNS -wiHA1MhX+i7Wf7/K0niq+k7hOkhleFkWgZtUq41gXh1VfOugka7UktYnk9mrBbAM -jmaloZNn2pMMAQxVg4ThiLm3zvuWqvXASWzUZc7IAd1GbN4AtDuhs252eqE9E4iT -Hk7F14wAS1JWqv666hReGHrmZJGx0xQTM9vPD1HN5t2U3KTfhO/mTlAUWVyg9tCt -OzboKgs1AgMBAAECggEBAKLj6IOJBKXolczpzb8UkyAjAkGBektcseV07gelJ/fk -3z0LuWPv5p12E/HlXB24vU2x/ikUbbP3eMsawRzDEahQqmNmPEkYAYUAy/Qpi9GN -DYvn3LqDec4jVgeQKS+p9H2DzUpTogp8zR2//yzbuWBg2+F//xh7vU0S0RQCziPM -x7RSBgbhxSfChfEJbS2sDnzfh0jRQmoY95iFv7puet1FJtzdZ4fgCd1RqmC2lFM5 -H0eZtN/Cz19lieVs0b996DErdEBqClVZO00eYbRozCDaBzRU3ybB/dMrGJxhkkXm -wb3kWMtziH9qOYsostuHIFu8eKFLloKxFnq2R4DGxOECgYEA2KUIZISOeGJSBcLJ -JAUK2gvgXPNo4HHWIwOA9xeN3ZJlsnPlffXQNnm6t1st1V2gfMm9I2n0m/F0y2B/ -n/XGSa8bghfPA9l0c2h58lkL3JQJR/paa8ycTz+YZPrznEyN7Qa0RrJXUvZv9lQL -Hc3+FHcSHgMqDV2f2bHAEu9YGi0CgYEAx6VEIPNvrHFgjo/jk1RTuk+m0xEWQsZL -Cs+izQMr2TaeJn8LG+93AvFuYn0J0nT3WuStLPrUg8i4IhSS6lf1tId5ivIZPm4r -YwMyblBJXhnHbk7Uqodjfw/3s6V2HAu++B7hTdyVr9DFuST9uv4m8bkPV8rfX1jE -I2rAPVWvgikCgYB+wNAQP547wQrMZBLbCDg5KwmyWJfb+b6X7czexOEz6humNTjo -YZHYzY/5B1fhpk3ntQD8X1nGg5caBvOk21+QbOtjShrM3cXMYCw5JvBRtitX+Zo9 -yBEMLOE0877ki8XeEDYZxu5gk98d+D4oygUGZEQtWxyXhVepPt5qNa8OYQKBgQDH -RVgZI6KFlqzv3wMh3PutbS9wYQ+9GrtwUQuIYe/0YSW9+vSVr5E0qNKrD28sV39F -hBauXLady0yvB6YUrjMbPFW+sCMuQzyfGWPO4+g3OrfqjFiM1ZIkE0YEU9Tt7XNx -qTDtTI1D7bhNMnTnniI1B6ge0und+3XafAThs5L48QKBgQCTTpfqMt8kU3tcI9sf -0MK03y7kA76d5uw0pZbWFy7KI4qnzWutCzb+FMPWWsoFtLJLPZy//u/ZCUVFVa4d -0Y/ASNQIESVPXFLAltlLo4MSmsg1vCBsbviEEaPeEjvMrgki93pYtd/aOSgkYC1T -mEq154s5rmqh+h+XRIf7Au0SLw== ------END PRIVATE KEY----- diff --git a/crates/boring/test/key.pem.pub b/crates/boring/test/key.pem.pub deleted file mode 100644 index 2a8225696..000000000 --- a/crates/boring/test/key.pem.pub +++ /dev/null @@ -1,9 +0,0 @@ ------BEGIN PUBLIC KEY----- -MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAr1bXMptaIgOL9PVL8a7W -KG/C8+IbxP018eMBQZT0SnPQmXp0Q8Aai/F+AEDE7b5sO5U7WdxU4GRYw0wqkQNF -si78KNfoj2ZMlx6NRfl4UKuzrpGTPgQxuKDYedngPpWcbmW4P3zEL2Y7b18n9NJr -atRUzH1Zh/ReRO525Xadu58aviPw1Mzgse7cKyzb03Gll9noLnYNIIpO8jL+QyrD -8qNmfacmR20U0a6XDTtmsmk7AitGETICbTT0KRf+oAP0yIHoonllPpNLUEPZQjrp -ClS/S/wKdj7gaq9TaMbHULhFMjbCV8cuPu//rUAuWp3riaznZGOVQyn3Dp2CB3ad -yQIDAQAB ------END PUBLIC KEY----- diff --git a/crates/boring/test/keystore-empty-chain.p12 b/crates/boring/test/keystore-empty-chain.p12 deleted file mode 100644 index c39930a5cb4f003f48ab94cc87f9435730540f24..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2514 zcmY+Ec{tPy7sm%PYGy29l6{>Jnwto_x^F7^PKPZobP|CE5VLn=X=W_4eD5|T+WbPU?{wz^ zH>yNRB3)^xvxrw`6y9QzEe&Ir3xrv&)=9$}Ojm zp87&GAzImu81Q6Or6jiZBY>eoBA1%QB6d{Jv>u<1l`Cb;gsPFlEaz{~(}R~?&nn-@ zf``oK!#6k^Q(`9^iuLc2oos8z%QGJ^X{^DhauJ?wl}EB6o1v&Mt_^YYcyTgf=vJJm zOYzw5`(x0ZukQ!rnPyLhV+2AIeG;tdyecnz*875iCFW6Sd^cU=R9r@h*H0N)Ygf(B zT{~hs_tAQ? zpqnyo)W(i0oo#W8v>#H{aCl(CnniSv5GL~TgsSL>+!;~GPHcVhyxEg9HT!O3hQ#Jz zK!UCDrnL>b^@p-9j}2QYI6-PF<9Z6No+Db2n^T0^>y--ZWJ6qyHSx5N^6d3FuJCVUF?n@%oB|JI?3ZQbL*|$Ze;*FF>BxCJY-woQq6^ya!)N6N!p{3 z!p}`FRd28+K^8$xOVhFLEPed+(#WMRzZ+n^UayqqfFZmN<#=v(RdARpWpN^-x);v% zX|2z6%W-|~o5uJRyU3~uhxqS}h}x1=EDHuFy$F#x)DBNnS8Rak=hQa1vczEgV;uo^ zSuX504fh%!tBmaN>C4KVQ4k^Le9H~Zy4<|gI#2BL($kTOa}L|ePk8vCxsdT{tR3be(;s@>0W_9e_qr=&0gJjch7@p zd24}H+J)D<$vVY37Jlc;^mJwk?iu#&8V5x#Eb|$iMxciAU;hknf`l{<+zg+Ysx!SQ z^kRmxSvtV;CaARArawN8oL28z@o;wTU8uRq1f4E7lAp}q8u;!*Wt${UiZip!lK6ej zppxlTYMR=H#}LH)+%G6^Hr7gwrvsg}h5tEp|H3TePt2kl$o8!oe@SY%#wJnH#R#F)9L!J5Mgk0_ z+A>$>d{P6qA0&iEAz6Nc&ac8-BMdsViNc z08tj)MOm~O*mj`P?Ym0EGqfmP+JDX+=jO^c&>~w>S2gzeG5j>WP~>e8sp2 zni?~rZG{z0z4Ww`jr0upA9Zpk*(RmjMkjtY+yn=@WhkL~-CUicfURyv&_xO;N@y2l zW9c^7s2esbFg=+rD^iM&iACCCN{q&*M>yQ0ij9ZrgCNy+qo3NGU%M5a(9Li+<)d|CUPcXZR9SLr)CR-ZO`nPVgUY+B&=tKL3}*w9s)m zi{{pf19HH=mC%1br%b9fz;mjvhG?c{o4L`7=RTOzBpLhj5+vp9y5pHW#E1J+kK2cA z*IBTj>qeI|de>hfmXnWAvs%`Xr^`NV7`v8?UA zgketBHv*Dh9>yESCZu548vQ;#S@`kr)g`-TgTT2;(dRnyOrvts=GMt@2}07C`M4Z6 zCsol(_oTozF{<%d9croZo4Re&wF~brEk?8#RkTyy=BQ5@ZKJ`Cg~P5Cw<_%)F+F9t z7W-F)$LWOCeU)Aky!00+))0>#-*m{Vqb1qpLvDba}yh0 zTEHEbx;=aAPbHEM@_f`dRPP&LNQUrrZ9p8q5=T`0)hbn0JtfNIjnox z>EEI=Ze~SK13M49-d&$kBOt<3&9k9a0P|d7N?y+QwntJ~9B&0j9K}X56I#~`3NM1` zijm?B8cr@(UH_HUVDw8UKFe%}zZ`8voa9cbWO`B4X0zc;_5jB5^nlh$(JX$y;o2(q z%r;kpWn6ZxR=C43P$<%a6aTylIIYlH zhcB!{Yo7-WBc(6KT}xXyvxYeb6r3uxVF*<}4-Z5N*0e}X8f+$AR)gdZ?W4zYuw9lm zSh=iB-Z)+y6lK>35p3;F8hN;}epWBQA~<#Eeo1Cgwf}=orERjV>$m_Hn4n!7Ga^oHlPN2+)Wr>;42PUb?>=NZxT zjeQ+;^D^lSC6so=(5n9(`d+IpogQhjuA#=TKg4%ZDJKsLa?P%7#3uv|XGf%28;4EG zp;}ZrpH;4Krr$*(Y9$!2dkRNnV5OqRE#Z?q3Yt@UyvBy52{NbFmJ?|eJ73|F>V;%b zWxsBqMJ3XcJ3wO2l@A$id$Lr0*qOrPDs9Xgs;(eHR?(_TGi**ozq0w%v3UZ4fdKFE zveNS_lp3vd%z!GG%VE%FAzev9(3r;{A5xXenPm5xG2r@HAl{;ySHN%BD<%DI@()eL z^tlPheDzjClo_OydkeitoRkL);!+EoaPZ zJ+@;XDQT4sA^nFGyponqwQrPEs?P#}fdIsONX~`2n}Z|Z(C&M?C3rht`CT{`s8~O> zuUoHbN)KdM4}5MGc9E!iC~D|pf#OXB5sJo(N(ze>e}LryvX~}JjUD*-}fV3oPy_GH~yRDej;@2z*qw38D?fIoHY5+%*D$ zfNQ)6VDnN_(~hFw$%IzQs|J~L&7c798cF^@QX+IBQPpDPmT{5iQcsK_ZiGpBMmv7L zEaU9RF7+3Z6V5@^lbty!j0B{>y!09tj(I{jI3{SVt5);2^^l3EH(Azz%O^ix9`{ z0)c=KT!UxWihsu=KAC|eAa=48mnzoE1hwL6PR+@+7KI_sl8v6FPRO{OT18S!El|Q8 zC5FznQMucjl~-%Zev#MtzV7D{ycin(H79?PT%WmuOozLSGRkfm5L<2yT;?3~(q^k< e{i>d=u^m@tp6-Ai2kOFUIU(JPR;qtZnHmW{L`6ve diff --git a/crates/boring/test/pkcs8.der b/crates/boring/test/pkcs8.der deleted file mode 100644 index 47a2db8ad99c55424b3eba09cf195d9a826d9f17..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1298 zcmV+t1?~DUf&~sRKn4jahDe6@4FLrWFf%Y41_>&LNQU&4g?6H{RVY&!jdHd z0tf&w6b1+?hDe6@4Fd-R2r=!d)&+UZtptJu$a&yr7EYiZl}%pX)s+2=UWe8d(yTAgryfPqPHtHz;;ifXGYSsJTOHe zI(!>JUNs`2U?!0HCoRn17PBy|;%NW)$%T0+U3=Lk)uHBwtQG#BiZAiFspr*Y#Gwy5 zGg42zET6*^K33jH; zvxc`zbs}$E+1Zrx^EoZ7I?_`<1WO<%uQ`!y2DW*}zQPv=DJ2aNno8b4{lR>aoG#-& zISxgK?dIbRXXCU)N6N{9YWlcnL2qC*_p8v+#EeJL8wvNwUv+Ho^C^3S+*D`!X#HK* zNT|q@IRB@1U7X8S4FxzyI3r3{gShC3{lvGvK}?kW4_2%xh@d!ppy-$^jxW!E^v@WO zU#-yV0a~)9TjWC+V@Y@d=F-Z5y9=oc)U?jDTsUgV-FrV)U{V|->seixdB?% zDJ?}s-jzNuXe+Z&Keo9AU6H^1`+}L%>*`d>yO<{mPg+Ki`CN&7am%+a2E9CGhh?@t z%)n;cRW0%gMLmykt(2+_$IH22606T=!BD&cMU`cx$HB08d7dzryn-T{V3foZ&fwkfAxTlY7JTX;!pY#?TqE zy<%dt>hj;;71!fhK2t-cHN1{mmS}OlH4QlquCHV||K1s#zRN0DN5loo%xB(NtrQ-I zK@As?EzP=W83$>T>dT32iOp(A2shg{-?K-FFcoqmT@9*IZ*}^C{Uo+kNGL5kIUKYC zn5#!5pEd&XST1fC33Ooy3gL(tpS`PKexi=nbJed=0QCYaqkEds9;Q5naavFIx<%dI IwLU>-bsH6IkN^Mx diff --git a/crates/boring/test/root-ca.key b/crates/boring/test/root-ca.key deleted file mode 100644 index 746ae2a68..000000000 --- a/crates/boring/test/root-ca.key +++ /dev/null @@ -1,27 +0,0 @@ ------BEGIN RSA PRIVATE KEY----- -MIIEpQIBAAKCAQEArVHWFn52Lbl1l59exduZntVSZyDYpzDND+S2LUcO6fRBWhV/ -1Kzox+2GZptbuMGmfI3iAnb0CFT4uC3kBkQQlXonGATSVyaFTFR+jq/lc0SP+9Bd -7SBXieIVeIXlY1TvlwIvj3Ntw9zX+scTA4SXxH6M0rKv9gTOub2vCMSHeF16X8DQ -r4XsZuQr7Cp7j1I4aqOJyap5JTl5ijmG8cnu0n+8UcRlBzy99dLWJG0AfI3VRJdW -pGTNVZ92aFff3RpK3F/WI2gp3qV1ynRAKuvmncGC3LDvYfcc2dgsc1N6Ffq8GIrk -gRob6eBcklDHp1d023Lwre+VaVDSo1//Y72UFwIDAQABAoIBAGZrnd/dC2kp11uq -Sg8SHk3GMdPPjTf/lq51sVJAU4fdV2Eso0XCiCzdKDcqR6F+jiu8jHp4YO0riW8N -b1pkjohGjyOaddIaaVsZ80/OkgDz20Ird9XQ7uoEODvopA12+755BDH5PDwqHVeM -nKfPiwAK6Jz6CxGO9bq9ZNoBiSyO1uofaB4Cpp8t74XVeAuPiI/Bb6WJ8TW5K5dt -x0Jihdo46QgZR+z4PnyWIoACkhSoQmtTb9NUrpKceBcxdCrZ/kEmYpnPq/PuSw6g -6HthjYP/H9Xulz69UR5Ez6z+1pU1rKFmQ46qK7X3zVHg233MlGekMzxdmShEjzCP -BMGYpQECgYEA5tqTZsUJwx3HDhkaZ/XOtaQqwOnZm9wPwTjGbV1t4+NUJzsl5gjP -ho+I8ZSGZ6MnNSh+ClpYhUHYBq0rTuAAYL2arcMOuOs1GrMmiZJbXm8zq8M7gYr5 -V99H/7akSx66WV/agPkLIvh/BWxlWgQcoVAIzZibbLUxr7Ye50pCLfECgYEAwDLn -mFz0mFMvGtaSp8RnTDTFCz9czCeDt0GujCxG1epdvtuxlg/S1QH+mGzA/AHkiu7z -uzCwGKWozNTdRkqVwYoJTB+AYHseSkuGP+a1zr39w+xBW/vESb2oP95GIwprXcG2 -b/qdeQVzuLQhYoqWI2u8CBwlHFfpQO4Bp2ea+ocCgYEAurIgLSfCqlpFpiAlG9hN -8NYwgU1d4E+LKj+JMd8yRO+PGh8amjub4X3pST5NqDjpN3Nk42iHWFWUqGmZsbM0 -ewg7tLUgDeqiStKBoxaK8AdMqWc9k5lZ53e6mZISsnHKUQdVBaLjH8gJqdAs8yyK -HudEB0mYwMSUxz6pJXIHrXECgYEAhJkaCpXm8chB8UQj/baUhZDKeI4IWZjRWHbq -Ey7g1+hPMMOk6yCTlf1ARqyRH8u2ftuIL5bRhs+Te21IE5yVYOb4rxn0mZuXNC6S -ujdTKwUMtESkeu9hZnaAQz/4J2ii1hY05WCDj+DhC4bKmY9/MYS8PuQb/kfwVqld -Xr8tvrUCgYEAmslHocXBUFXyRDkEOx/aKo+t9fPBr95PBZzFUt9ejrTP4PXsLa46 -3/PNOCGdrQxh5qHHcvLwR4bPL++Dj+qMUTJXANrArKPDpE2WqH6pqWIC6yaZvzUk -17QbpXR6bHcdJV045pWpw40UCStTocVynY1lBfOw8VqxBIBlpVBBzew= ------END RSA PRIVATE KEY----- diff --git a/crates/boring/test/root-ca.pem b/crates/boring/test/root-ca.pem deleted file mode 100644 index 4ec2f5388..000000000 --- a/crates/boring/test/root-ca.pem +++ /dev/null @@ -1,21 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIDXTCCAkWgAwIBAgIJAOIvDiVb18eVMA0GCSqGSIb3DQEBCwUAMEUxCzAJBgNV -BAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEwHwYDVQQKDBhJbnRlcm5ldCBX -aWRnaXRzIFB0eSBMdGQwHhcNMTYwODE0MTY1NjExWhcNMjYwODEyMTY1NjExWjBF -MQswCQYDVQQGEwJBVTETMBEGA1UECAwKU29tZS1TdGF0ZTEhMB8GA1UECgwYSW50 -ZXJuZXQgV2lkZ2l0cyBQdHkgTHRkMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB -CgKCAQEArVHWFn52Lbl1l59exduZntVSZyDYpzDND+S2LUcO6fRBWhV/1Kzox+2G -ZptbuMGmfI3iAnb0CFT4uC3kBkQQlXonGATSVyaFTFR+jq/lc0SP+9Bd7SBXieIV -eIXlY1TvlwIvj3Ntw9zX+scTA4SXxH6M0rKv9gTOub2vCMSHeF16X8DQr4XsZuQr -7Cp7j1I4aqOJyap5JTl5ijmG8cnu0n+8UcRlBzy99dLWJG0AfI3VRJdWpGTNVZ92 -aFff3RpK3F/WI2gp3qV1ynRAKuvmncGC3LDvYfcc2dgsc1N6Ffq8GIrkgRob6eBc -klDHp1d023Lwre+VaVDSo1//Y72UFwIDAQABo1AwTjAdBgNVHQ4EFgQUbNOlA6sN -XyzJjYqciKeId7g3/ZowHwYDVR0jBBgwFoAUbNOlA6sNXyzJjYqciKeId7g3/Zow -DAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEAVVaR5QWLZIRR4Dw6TSBn -BQiLpBSXN6oAxdDw6n4PtwW6CzydaA+creiK6LfwEsiifUfQe9f+T+TBSpdIYtMv -Z2H2tjlFX8VrjUFvPrvn5c28CuLI0foBgY8XGSkR2YMYzWw2jPEq3Th/KM5Catn3 -AFm3bGKWMtGPR4v+90chEN0jzaAmJYRrVUh9vea27bOCn31Nse6XXQPmSI6Gyncy -OAPUsvPClF3IjeL1tmBotWqSGn1cYxLo+Lwjk22A9h6vjcNQRyZF2VLVvtwYrNU3 -mwJ6GCLsLHpwW/yjyvn8iEltnJvByM/eeRnfXV6WDObyiZsE/n6DxIRJodQzFqy9 -GA== ------END CERTIFICATE----- diff --git a/crates/boring/test/rsa-encrypted.pem b/crates/boring/test/rsa-encrypted.pem deleted file mode 100644 index a62499972..000000000 --- a/crates/boring/test/rsa-encrypted.pem +++ /dev/null @@ -1,30 +0,0 @@ ------BEGIN RSA PRIVATE KEY----- -Proc-Type: 4,ENCRYPTED -DEK-Info: AES-128-CBC,E2F16153E2BA3D617285A68C896BA6AF - -vO9SnhtGjGe8pG1pN//vsONnvJr+DjU+lFCiSqGMPT7tezDnbehLfS+9kus2HV7r -HmI14JvVG9O7NpF7zMyBRlHYdWcCCWED9Yar0NsWN9419e5pMe/bqIXAzAiJbtT4 -OB9U5XF3m+349zjN1dVXPPLGRmMC1pcHAlofeb5nIUFTvUi5xcsbe1itGjgkkvHb -Bt8NioHTBun8kKrlsFQOuB55ylBU/eWG8DQBtvFOmQ7iWp0RnGQfh8k5e5rcZNpQ -fD9ygc7UVISl0xTrIG4IH15g34H+nrBauKtIPOpNPuXQPOMHCZv3XH8wnhrWHHwT -ZFnQBdXbSpQtMsRh0phG2G+VIlyCgSn4+CxjCJ+TgFtsoK/tU0unmRYc59QnTxxb -qkHYsPs3E0NApQAgH1ENEGl1M+FGLYQH7gftjc3ophBTeRA17sRmD7Y4QBInggsq -Gv6tImPVBdekAjz/Ls/EyMwjAvvrL5eAokqrIsAarGo+zmbJKHzknw2KUz2En0+k -YYaxB4oy9u7bzuQlvio6xYHJEb4K197bby4Dldmqv7YCCJBJwhOBAInMD687viKv -vcUwL8YuS6cW5E8MbvEENlY4+lvKKj3M8Bnyb79cYIPQe92EuCwXU9DZXPRMLwwM -oFEJpF5E/PmNJzu+B52ahHtDrh83WSx71fWqjdTqwkPZhAYo3ztsfFkb/UqUcq8u -rBSebeUjZh0XZ9B04eshZQ5vJUcXGtYIe/77beV3Pv89/fw+zTZjpiP9Q3sZALzf -Qt0YGp0/6qBuqR1tcqdu65AS2hun7yFw7uRavqYKvww4axRiz2do+xWmZFuoCAwD -EWktaUujltpvAc1lo7lg4C6nByefJB9Xqk22N/vpqOsWr1NbAntT42Qj/HF9BVWR -osvN3yMnKYWYe6oSTVnNBDM5obWAIHd3I9gcxTOTb1KsEwt2RrDs5EpB5ptS3Fjo -JfBRhNZQ3cXttrIIhsHgDn9BDNg865/xpIgktKj0gEd60Abx0PqkAIm6IZTh4Efg -7uZwfzxB+saOcddbrW2gNdzVZMC0s2Ye3sqHhtLbAJ3BlXYTxE4CAvTg54Ny+5hF -IjvjlOKgXceSG1cSfk21/wyp9RY3Ft0AEYvvp0kZScWZaoA2aSFDUrchXVhgrEbn -lJ7UptjefwRFIreAlwbKSbIDDNWnyzvIWyHfQ2aYqgnb7W7XqNPSgH9cALCfzirI -dlRHjha0bMUtrjPCC/YfMXzJBVniy0gG6Pd5uC7vz/Awn6/6HRQVNaTQASphPBQ7 -bJuz+JTfzI9OUVCMRMdnb6b35U4P9tibFmnPvzTIPe+3WUmf8aRsLS3NN3G1Webd -PMYVZpMycPaAI0Ht87axhsOzlxCWHYWjdHa+WoNNc1J90TxLCmAHquh5BDaWvjMK -0DySftJZjV7Tf1p2KosmU83LRl39B5NHMbZb1xOEZl9IWwhT/PVKTVZ25xdxWLfb -hF4l8rfvKehIp5r4t8zW1bvI2Hl6vrUvmcUVWt3BfKjxlgwRVD0vvwonMt1INesF -204vUBeXbDsUUicLwOyUgaFvJ3XU3dOyvL9MhOgM5OgoFRRhG+4AS8a5JCD8iLtq ------END RSA PRIVATE KEY----- diff --git a/crates/boring/test/rsa.pem b/crates/boring/test/rsa.pem deleted file mode 100644 index d8185fed6..000000000 --- a/crates/boring/test/rsa.pem +++ /dev/null @@ -1,27 +0,0 @@ ------BEGIN RSA PRIVATE KEY----- -MIIEowIBAAKCAQEAofgWCuLjybRlzo0tZWJjNiuSfb4p4fAkd/wWJcyQoTbji9k0 -l8W26mPddxHmfHQp+Vaw+4qPCJrcS2mJPMEzP1Pt0Bm4d4QlL+yRT+SFd2lZS+pC -gNMsD1W/YpRPEwOWvG6b32690r2jZ47soMZo9wGzjb/7OMg0LOL+bSf63kpaSHSX -ndS5z5rexMdbBYUsLA9e+KXBdQOS+UTo7WTBEMa2R2CapHg665xsmtdVMTBQY4uD -Zlxvb3qCo5ZwKh9kG4LT6/I5IhlJH7aGhyxXFvUK+DWNmoudF8NAco9/h9iaGNj8 -q2ethFkMLs91kzk2PAcDTW9gb54h4FRWyuXpoQIDAQABAoIBABKucaRpzQorw35S -bEUAVx8dYXUdZOlJcHtiWQ+dC6V8ljxAHj/PLyzTveyI5QO/xkObCyjIL303l2cf -UhPu2MFaJdjVzqACXuOrLot/eSFvxjvqVidTtAZExqFRJ9mylUVAoLvhowVWmC1O -n95fZCXxTUtxNEG1Xcc7m0rtzJKs45J+N/V9DP1edYH6USyPSWGp6wuA+KgHRnKK -Vf9GRx80JQY7nVNkL17eHoTWEwga+lwi0FEoW9Y7lDtWXYmKBWhUE+U8PGxlJf8f -40493HDw1WRQ/aSLoS4QTp3rn7gYgeHEvfJdkkf0UMhlknlo53M09EFPdadQ4TlU -bjqKc50CgYEA4BzEEOtIpmVdVEZNCqS7baC4crd0pqnRH/5IB3jw3bcxGn6QLvnE -tfdUdiYrqBdss1l58BQ3KhooKeQTa9AB0Hw/Py5PJdTJNPY8cQn7ouZ2KKDcmnPG -BY5t7yLc1QlQ5xHdwW1VhvKn+nXqhJTBgIPgtldC+KDV5z+y2XDwGUcCgYEAuQPE -fgmVtjL0Uyyx88GZFF1fOunH3+7cepKmtH4pxhtCoHqpWmT8YAmZxaewHgHAjLYs -p1ZSe7zFYHj7C6ul7TjeLQeZD/YwD66t62wDmpe/HlB+TnBA+njbglfIsRLtXlnD -zQkv5dTltRJ11BKBBypeeF6689rjcJIDEz9RWdcCgYAHAp9XcCSrn8wVkMVkKdb7 -DOX4IKjzdahm+ctDAJN4O/y7OW5FKebvUjdAIt2GuoTZ71iTG+7F0F+lP88jtjP4 -U4qe7VHoewl4MKOfXZKTe+YCS1XbNvfgwJ3Ltyl1OH9hWvu2yza7q+d5PCsDzqtm -27kxuvULVeya+TEdAB1ijQKBgQCH/3r6YrVH/uCWGy6bzV1nGNOdjKc9tmkfOJmN -54dxdixdpozCQ6U4OxZrsj3FcOhHBsqAHvX2uuYjagqvo3cOj1TRqNocX40omfCC -Mx3bD1yPPf/6TI2XECva/ggqEY2mYzmIiA5LVVmc5nrybr+lssFKneeyxN2Wq93S -0iJMdQKBgCGHewxzoa1r8ZMD0LETNrToK423K377UCYqXfg5XMclbrjPbEC3YI1Z -NqMtuhdBJqUnBi6tjKMF+34Xf0CUN8ncuXGO2CAYvO8PdyCixHX52ybaDjy1FtCE -6yUXjoKNXKvUm7MWGsAYH6f4IegOetN5NvmUMFStCSkh7ixZLkN1 ------END RSA PRIVATE KEY----- diff --git a/crates/boring/test/rsa.pem.pub b/crates/boring/test/rsa.pem.pub deleted file mode 100644 index 093f2ba12..000000000 --- a/crates/boring/test/rsa.pem.pub +++ /dev/null @@ -1,9 +0,0 @@ ------BEGIN PUBLIC KEY----- -MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAofgWCuLjybRlzo0tZWJj -NiuSfb4p4fAkd/wWJcyQoTbji9k0l8W26mPddxHmfHQp+Vaw+4qPCJrcS2mJPMEz -P1Pt0Bm4d4QlL+yRT+SFd2lZS+pCgNMsD1W/YpRPEwOWvG6b32690r2jZ47soMZo -9wGzjb/7OMg0LOL+bSf63kpaSHSXndS5z5rexMdbBYUsLA9e+KXBdQOS+UTo7WTB -EMa2R2CapHg665xsmtdVMTBQY4uDZlxvb3qCo5ZwKh9kG4LT6/I5IhlJH7aGhyxX -FvUK+DWNmoudF8NAco9/h9iaGNj8q2ethFkMLs91kzk2PAcDTW9gb54h4FRWyuXp -oQIDAQAB ------END PUBLIC KEY----- From b7082d2722a6af7f39e1a394ea228f16455c1ed2 Mon Sep 17 00:00:00 2001 From: Tom Gouville Date: Thu, 28 Mar 2024 14:46:29 +0100 Subject: [PATCH 53/63] making some tests less brittle by increasing the number of retries --- tlspuffin/src/tls/vulnerabilities.rs | 32 ++++++++++++++-------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/tlspuffin/src/tls/vulnerabilities.rs b/tlspuffin/src/tls/vulnerabilities.rs index dbab0d94a..db2000d57 100644 --- a/tlspuffin/src/tls/vulnerabilities.rs +++ b/tlspuffin/src/tls/vulnerabilities.rs @@ -1116,8 +1116,8 @@ pub mod tests { expect_trace_crash( seed_freak.build_trace(), PutOptions::default(), - Some(std::time::Duration::from_secs(5)), - Some(5), + Some(std::time::Duration::from_secs(10)), + Some(20), ); } @@ -1132,8 +1132,8 @@ pub mod tests { expect_trace_crash( seed_heartbleed.build_trace(), PutOptions::default(), - Some(std::time::Duration::from_secs(5)), - Some(5), + Some(std::time::Duration::from_secs(10)), + Some(20), ); } @@ -1148,8 +1148,8 @@ pub mod tests { expect_trace_crash( seed_cve_2021_3449.build_trace(), PutOptions::default(), - Some(std::time::Duration::from_secs(5)), - Some(5), + Some(std::time::Duration::from_secs(10)), + Some(20), ); } @@ -1204,8 +1204,8 @@ pub mod tests { expect_trace_crash( seed_session_resumption_dhe_full.build_trace(), PutOptions::from_slice_vec(vec![("use_clear", &true.to_string())]), - Some(std::time::Duration::from_secs(5)), - Some(5), + Some(std::time::Duration::from_secs(10)), + Some(20), ); } @@ -1225,8 +1225,8 @@ pub mod tests { expect_trace_crash( seed_cve_2022_38153.build_trace(), PutOptions::default(), - Some(std::time::Duration::from_secs(5)), - Some(5), + Some(std::time::Duration::from_secs(10)), + Some(20), ); } @@ -1245,8 +1245,8 @@ pub mod tests { expect_trace_crash( seed_cve_2022_39173.build_trace(), PutOptions::default(), - Some(std::time::Duration::from_secs(5)), - Some(5), + Some(std::time::Duration::from_secs(10)), + Some(20), ); } @@ -1265,8 +1265,8 @@ pub mod tests { expect_trace_crash( seed_cve_2022_39173_full.build_trace(), PutOptions::default(), - Some(std::time::Duration::from_secs(5)), - Some(5), + Some(std::time::Duration::from_secs(10)), + Some(20), ); } @@ -1285,8 +1285,8 @@ pub mod tests { expect_trace_crash( seed_cve_2022_39173_minimized.build_trace(), PutOptions::default(), - Some(std::time::Duration::from_secs(5)), - Some(5), + Some(std::time::Duration::from_secs(10)), + Some(20), ); } From 4c130f26fd5152c522629a6dfadecb90788de594 Mon Sep 17 00:00:00 2001 From: Tom Gouville Date: Thu, 28 Mar 2024 15:05:17 +0100 Subject: [PATCH 54/63] increase expect_trace_crash timeout --- tlspuffin/src/tls/vulnerabilities.rs | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/tlspuffin/src/tls/vulnerabilities.rs b/tlspuffin/src/tls/vulnerabilities.rs index db2000d57..7b60e2a69 100644 --- a/tlspuffin/src/tls/vulnerabilities.rs +++ b/tlspuffin/src/tls/vulnerabilities.rs @@ -1116,7 +1116,7 @@ pub mod tests { expect_trace_crash( seed_freak.build_trace(), PutOptions::default(), - Some(std::time::Duration::from_secs(10)), + Some(std::time::Duration::from_secs(20)), Some(20), ); } @@ -1132,7 +1132,7 @@ pub mod tests { expect_trace_crash( seed_heartbleed.build_trace(), PutOptions::default(), - Some(std::time::Duration::from_secs(10)), + Some(std::time::Duration::from_secs(20)), Some(20), ); } @@ -1148,7 +1148,7 @@ pub mod tests { expect_trace_crash( seed_cve_2021_3449.build_trace(), PutOptions::default(), - Some(std::time::Duration::from_secs(10)), + Some(std::time::Duration::from_secs(20)), Some(20), ); } @@ -1204,7 +1204,7 @@ pub mod tests { expect_trace_crash( seed_session_resumption_dhe_full.build_trace(), PutOptions::from_slice_vec(vec![("use_clear", &true.to_string())]), - Some(std::time::Duration::from_secs(10)), + Some(std::time::Duration::from_secs(20)), Some(20), ); } @@ -1225,7 +1225,7 @@ pub mod tests { expect_trace_crash( seed_cve_2022_38153.build_trace(), PutOptions::default(), - Some(std::time::Duration::from_secs(10)), + Some(std::time::Duration::from_secs(20)), Some(20), ); } @@ -1245,7 +1245,7 @@ pub mod tests { expect_trace_crash( seed_cve_2022_39173.build_trace(), PutOptions::default(), - Some(std::time::Duration::from_secs(10)), + Some(std::time::Duration::from_secs(20)), Some(20), ); } @@ -1265,7 +1265,7 @@ pub mod tests { expect_trace_crash( seed_cve_2022_39173_full.build_trace(), PutOptions::default(), - Some(std::time::Duration::from_secs(10)), + Some(std::time::Duration::from_secs(20)), Some(20), ); } @@ -1285,7 +1285,7 @@ pub mod tests { expect_trace_crash( seed_cve_2022_39173_minimized.build_trace(), PutOptions::default(), - Some(std::time::Duration::from_secs(10)), + Some(std::time::Duration::from_secs(20)), Some(20), ); } From dfe3703ad776010f8a1c00c84593bf0a0402e414 Mon Sep 17 00:00:00 2001 From: Tom Gouville Date: Thu, 28 Mar 2024 15:12:45 +0100 Subject: [PATCH 55/63] removing useless env var in boringssl-src --- crates/boringssl-src/src/main.rs | 4 ---- 1 file changed, 4 deletions(-) diff --git a/crates/boringssl-src/src/main.rs b/crates/boringssl-src/src/main.rs index 76741be24..22a12bc53 100644 --- a/crates/boringssl-src/src/main.rs +++ b/crates/boringssl-src/src/main.rs @@ -3,12 +3,8 @@ use std::{env, path::PathBuf}; use boringssl_src::{build, BoringSSLOptions, GitRef}; fn main() { - // let out_dir = PathBuf::from("out_dir"); let out_dir = PathBuf::from(env::var("CARGO_MANIFEST_DIR").unwrap()).join("out"); - env::set_var("TARGET", "aarch64-apple-darwin"); - env::set_var("HOST", "aarch64-apple-darwin"); - env::set_var("OPT_LEVEL", "3"); env::set_var( "OUT_DIR", &out_dir.canonicalize().unwrap().display().to_string(), From 5594a263e3b3639ca70691493d02ed827a50389f Mon Sep 17 00:00:00 2001 From: Tom Gouville Date: Thu, 28 Mar 2024 15:59:19 +0100 Subject: [PATCH 56/63] replacing BUG tag with TODO --- tlspuffin/src/boringssl/deterministic.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tlspuffin/src/boringssl/deterministic.rs b/tlspuffin/src/boringssl/deterministic.rs index 9a8644f98..4067524e2 100644 --- a/tlspuffin/src/boringssl/deterministic.rs +++ b/tlspuffin/src/boringssl/deterministic.rs @@ -25,7 +25,7 @@ mod tests { }, }; - // BUG: This test only works in a single threaded cargo test execution + // TODO: This test only works in a single threaded cargo test execution #[ignore] #[test] fn test_boringssl_no_randomness_full() { From f73c985981d6ea80bcbea7a59325ed85dfd4a5de Mon Sep 17 00:00:00 2001 From: Tom Gouville Date: Thu, 28 Mar 2024 16:27:52 +0100 Subject: [PATCH 57/63] cleaning deterministic_rand.c --- crates/openssl-src-111/src/deterministic_rand.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/crates/openssl-src-111/src/deterministic_rand.c b/crates/openssl-src-111/src/deterministic_rand.c index 92f63d70f..b5a51ed4f 100644 --- a/crates/openssl-src-111/src/deterministic_rand.c +++ b/crates/openssl-src-111/src/deterministic_rand.c @@ -1,11 +1,7 @@ // based on https://stackoverflow.com/a/7510354 #include -#include unsigned int tlspuffin_seed = 42; -const unsigned int m = 0xFFFFFFFF; -const unsigned int a = 22695477; -const unsigned int c = 1; #define UNUSED(x) (void)(x) From 7118c0cfb4e9849737f6792e742620d67da97a3c Mon Sep 17 00:00:00 2001 From: Tom Gouville Date: Fri, 29 Mar 2024 13:13:54 +0100 Subject: [PATCH 58/63] changing BUG tag to TODO --- tlspuffin/src/tls/seeds.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tlspuffin/src/tls/seeds.rs b/tlspuffin/src/tls/seeds.rs index f3e536fec..f9314e7e1 100644 --- a/tlspuffin/src/tls/seeds.rs +++ b/tlspuffin/src/tls/seeds.rs @@ -936,7 +936,7 @@ pub fn seed_client_attacker_auth(server: AgentName) -> Trace { } } -// BUG: `BAD_SIGNATURE` error +// TODO: `BAD_SIGNATURE` error pub fn seed_client_attacker_auth_boring(server: AgentName) -> Trace { let client_hello = term! { fn_client_hello( From 98f50d432bc105f16678de6d40bb260a658554d3 Mon Sep 17 00:00:00 2001 From: Tom Gouville Date: Fri, 29 Mar 2024 13:43:29 +0100 Subject: [PATCH 59/63] deactivate lto in debug mode and add a note about debug build w/ BoringSSL and ASAN --- Cargo.toml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 76854c0d8..b001bdb39 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -51,7 +51,7 @@ debug = true [profile.dev] panic = "abort" -# TODO: Without LTO, the fuzzer hangs in debug releases with BoringSSL -# To reproduce the bug, simply disable lto and run -# `cargo run --bin tlspuffin --features boringssl202403` -lto = true +# Without LTO, the fuzzer hangs in debug releases with BoringSSL +# and ASAN. If you want to do a debug build with BoringSSL and ASAN +# you need to activate LTO. +# lto = true From 132a10e66ce2dc2ffcf84f681eb05ae87879b98e Mon Sep 17 00:00:00 2001 From: Tom Gouville Date: Fri, 29 Mar 2024 13:44:24 +0100 Subject: [PATCH 60/63] remove useless line --- tlspuffin/src/boringssl/mod.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/tlspuffin/src/boringssl/mod.rs b/tlspuffin/src/boringssl/mod.rs index a5878e49d..899eab342 100644 --- a/tlspuffin/src/boringssl/mod.rs +++ b/tlspuffin/src/boringssl/mod.rs @@ -128,7 +128,6 @@ impl Stream for BoringSSL { &mut self, ) -> Result>, Error> { let memory_stream = self.stream.get_mut(); - //memory_stream.take_message_from_outbound() MemoryStream::take_message_from_outbound(memory_stream) } From cc6b8ba56c3e25e0705f2172662fc02fdae55005 Mon Sep 17 00:00:00 2001 From: Tom Gouville Date: Fri, 29 Mar 2024 13:45:07 +0100 Subject: [PATCH 61/63] add BoringSSL to README --- README.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/README.md b/README.md index 19c1951f6..2dd975bb4 100644 --- a/README.md +++ b/README.md @@ -87,6 +87,8 @@ coverage, expressiveness of executable protocol traces and stable and extensible * OpenSSL 1.0.1f, 1.0.2u, 1.1.1k * LibreSSL 3.3.3 * wolfSSL 5.1.0 - 5.4.0 + * BoringSSL (last commit tested 368d0d87d0bd00f8227f74ce18e8e4384eaf6afa) + - Disclaimer : there is a bug will building in debug mode with asan (set `lto=true` in `Cargo.toml` to circumvent) * Reproducible for each LUT. We use sources from forks this are in the [tlspuffin organisation](https://github.com/tlspuffin) * 70% Test Coverage * Writtin in Rust! @@ -105,6 +107,10 @@ WolfSSL: * autoconf * libtool +BoringSSL: +* go +* cmake + For the python `tlspuffin-analyzer`: * libyajl-dev * `wheel` from Python pip From da6c516901fe5358bf9f19979f049103dd34629b Mon Sep 17 00:00:00 2001 From: Tom Gouville Date: Fri, 29 Mar 2024 14:38:15 +0100 Subject: [PATCH 62/63] using deterministic_rand.c from #283 --- .../openssl-src-111/src/deterministic_rand.c | 32 +++++++++++-------- tlspuffin/src/openssl/deterministic.rs | 32 ++++--------------- tlspuffin/src/openssl/mod.rs | 6 ++-- 3 files changed, 29 insertions(+), 41 deletions(-) diff --git a/crates/openssl-src-111/src/deterministic_rand.c b/crates/openssl-src-111/src/deterministic_rand.c index b5a51ed4f..afed38e1d 100644 --- a/crates/openssl-src-111/src/deterministic_rand.c +++ b/crates/openssl-src-111/src/deterministic_rand.c @@ -1,19 +1,20 @@ // based on https://stackoverflow.com/a/7510354 #include +#include -unsigned int tlspuffin_seed = 42; +static uint64_t seed = 42; #define UNUSED(x) (void)(x) // Seed the RNG. srand() takes an unsigned int, so we just use the first -// sizeof(unsigned int) bytes in the buffer to seed the RNG. +// sizeof(uint64_t) bytes in the buffer to seed the RNG. static int stdlib_rand_seed(const void *buf, int num) { - if (num < 1) + if (num < sizeof(uint64_t)) { return 0; } - tlspuffin_seed = *((unsigned int *) buf); + seed = *((uint64_t *) buf); return 1; } @@ -23,13 +24,16 @@ static int stdlib_rand_bytes(unsigned char *buf, int num) { for (int index = 0; index < num; ++index) { - tlspuffin_seed = 6364136223846793005ULL*tlspuffin_seed + 1; - buf[index] = tlspuffin_seed>>33; + seed = 6364136223846793005ULL*seed + 1; + buf[index] = seed>>33; } return 1; } -static void stdlib_rand_cleanup() {} +static void stdlib_rand_cleanup() +{ +} + static int stdlib_rand_add(const void *buf, int num, double add_entropy) { UNUSED(buf); @@ -37,17 +41,19 @@ static int stdlib_rand_add(const void *buf, int num, double add_entropy) UNUSED(add_entropy); return 1; } + static int stdlib_rand_status() { return 1; } -RAND_METHOD stdlib_rand_meth = { stdlib_rand_seed, - stdlib_rand_bytes, - stdlib_rand_cleanup, - stdlib_rand_add, - stdlib_rand_bytes, - stdlib_rand_status +RAND_METHOD stdlib_rand_meth = { + stdlib_rand_seed, + stdlib_rand_bytes, + stdlib_rand_cleanup, + stdlib_rand_add, + stdlib_rand_bytes, + stdlib_rand_status, }; void make_openssl_deterministic() diff --git a/tlspuffin/src/openssl/deterministic.rs b/tlspuffin/src/openssl/deterministic.rs index 14829a287..76f5b2585 100644 --- a/tlspuffin/src/openssl/deterministic.rs +++ b/tlspuffin/src/openssl/deterministic.rs @@ -1,34 +1,21 @@ use std::os::raw::c_int; -use log::{trace, warn}; +use log::warn; #[cfg(feature = "deterministic")] extern "C" { fn make_openssl_deterministic(); fn RAND_seed(buf: *mut u8, num: c_int); - pub static mut tlspuffin_seed: u32; } -pub fn get_seed() -> u32 { - unsafe { return tlspuffin_seed } -} #[cfg(feature = "deterministic")] -pub fn determinism_set_reseed_openssl() { - trace!("Making OpenSSL fully deterministic: reset rand and reseed to a constant..."); +pub fn set_openssl_deterministic() { + warn!("OpenSSL is no longer random!"); unsafe { make_openssl_deterministic(); - } - determinism_reseed_openssl(); -} - -#[cfg(feature = "deterministic")] -pub fn determinism_reseed_openssl() { - trace!(" - Reseed RAND for OpenSSL"); - unsafe { let mut seed: [u8; 4] = 42u32.to_le().to_ne_bytes(); let buf = seed.as_mut_ptr(); RAND_seed(buf, 4); - tlspuffin_seed = 42 as u32; } } @@ -36,18 +23,13 @@ pub fn determinism_reseed_openssl() { mod tests { use openssl::rand::rand_bytes; - use crate::openssl::deterministic::{determinism_set_reseed_openssl, get_seed}; - #[test] #[cfg(feature = "openssl111-binding")] - fn test_openssl_no_randomness_simple() { - determinism_set_reseed_openssl(); - assert_eq!(get_seed(), 42); + fn test_openssl_no_randomness() { + use crate::openssl::deterministic::set_openssl_deterministic; + set_openssl_deterministic(); let mut buf1 = [0; 2]; rand_bytes(&mut buf1).unwrap(); - assert_eq!(buf1, [177, 180]); - assert_ne!(get_seed(), 42); - determinism_set_reseed_openssl(); - assert_eq!(get_seed(), 42); + assert_eq!(buf1, [183, 96]); } } diff --git a/tlspuffin/src/openssl/mod.rs b/tlspuffin/src/openssl/mod.rs index 46bd32ab9..9fcc9a9c7 100644 --- a/tlspuffin/src/openssl/mod.rs +++ b/tlspuffin/src/openssl/mod.rs @@ -93,13 +93,13 @@ pub fn new_openssl_factory() -> Box> { fn determinism_set_reseed(&self) -> () { debug!("[Determinism] set and reseed"); #[cfg(feature = "deterministic")] - deterministic::determinism_set_reseed_openssl(); + deterministic::set_openssl_deterministic(); } fn determinism_reseed(&self) -> () { debug!("[Determinism] reseed"); #[cfg(feature = "deterministic")] - deterministic::determinism_reseed_openssl(); + deterministic::set_openssl_deterministic(); } } @@ -223,7 +223,7 @@ impl Put for OpenSSL { fn determinism_reseed(&mut self) -> Result<(), Error> { #[cfg(feature = "deterministic")] { - deterministic::determinism_reseed_openssl(); + deterministic::set_openssl_deterministic(); Ok(()) } #[cfg(not(feature = "deterministic"))] From 0d590fb34ca3b85c59d0efeadea6c2fc1e56433e Mon Sep 17 00:00:00 2001 From: Tom Gouville Date: Fri, 29 Mar 2024 14:43:03 +0100 Subject: [PATCH 63/63] removing attempt to make timestamp deterministic in openssl --- .../patches/det_timestamp.patch | 54 ------------------- crates/openssl-src-111/src/openssl.rs | 20 ++++--- 2 files changed, 9 insertions(+), 65 deletions(-) delete mode 100644 crates/openssl-src-111/patches/det_timestamp.patch diff --git a/crates/openssl-src-111/patches/det_timestamp.patch b/crates/openssl-src-111/patches/det_timestamp.patch deleted file mode 100644 index 8009fbc41..000000000 --- a/crates/openssl-src-111/patches/det_timestamp.patch +++ /dev/null @@ -1,54 +0,0 @@ -Subject: [PATCH] deterministic timestamp ---- -Index: crypto/time.c -IDEA additional info: -Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP -<+>UTF-8 -=================================================================== -diff --git a/crypto/time.c b/crypto/time.c ---- a/crypto/time.c (revision f925bfebbb287321133b9251e72bee869a0f58b4) -+++ b/crypto/time.c (date 1703166565605) -@@ -11,6 +11,9 @@ - #include - #include "internal/time.h" - -+unsigned int tlspuffin_time = 42; -+ -+ - OSSL_TIME ossl_time_now(void) - { - OSSL_TIME r; -@@ -32,17 +35,21 @@ - # endif - r.t = ((uint64_t)now.ul) * (OSSL_TIME_SECOND / 10000000); - #else /* defined(_WIN32) */ -- struct timeval t; -- -- if (gettimeofday(&t, NULL) < 0) { -- ERR_raise_data(ERR_LIB_SYS, get_last_sys_error(), -- "calling gettimeofday()"); -- return ossl_time_zero(); -- } -- if (t.tv_sec <= 0) -- r.t = t.tv_usec <= 0 ? 0 : t.tv_usec * OSSL_TIME_US; -- else -- r.t = ((uint64_t)t.tv_sec * 1000000 + t.tv_usec) * OSSL_TIME_US; -+// struct timeval t; -+// -+// if (gettimeofday(&t, NULL) < 0) { -+// ERR_raise_data(ERR_LIB_SYS, get_last_sys_error(), -+// "calling gettimeofday()"); -+// return ossl_time_zero(); -+// } -+// if (t.tv_sec <= 0) -+// r.t = t.tv_usec <= 0 ? 0 : t.tv_usec * OSSL_TIME_US; -+// else -+// r.t = ((uint64_t)t.tv_sec * 1000000 + t.tv_usec) * OSSL_TIME_US; - #endif /* defined(_WIN32) */ -- return r; -+ r = ossl_time_zero(); -+ r.t = r.t + tlspuffin_time * (1000 * OSSL_TIME_US); // 1 seconds elapsed between every call -+ tlspuffin_time++; -+ return ossl_time_zero(); -+// return r; - } diff --git a/crates/openssl-src-111/src/openssl.rs b/crates/openssl-src-111/src/openssl.rs index 286703473..829cb47d8 100644 --- a/crates/openssl-src-111/src/openssl.rs +++ b/crates/openssl-src-111/src/openssl.rs @@ -1,8 +1,15 @@ extern crate bindgen; extern crate cc; -use std::{env, fs, fs::{canonicalize, File}, io, io::Write, path::{Path, PathBuf}, process::Command}; use std::io::ErrorKind; +use std::{ + env, fs, + fs::{canonicalize, File}, + io, + io::Write, + path::{Path, PathBuf}, + process::Command, +}; const REF: &str = if cfg!(feature = "openssl101f") { "OpenSSL_1_0_1f" @@ -41,10 +48,7 @@ pub fn version() -> &'static str { env!("CARGO_PKG_VERSION") } -fn patch_openssl>( - out_dir: P, - patch: &str, -) -> std::io::Result<()> { +fn patch_openssl>(out_dir: P, patch: &str) -> std::io::Result<()> { let root = Path::new(env!("CARGO_MANIFEST_DIR")); let status = Command::new("git") .current_dir(out_dir) @@ -147,12 +151,6 @@ impl Build { clone_repo(&inner_dir.to_str().unwrap()).unwrap(); - if cfg!(feature = "openssl312") { - let source_dir = PathBuf::from(env::var("CARGO_MANIFEST_DIR").unwrap()); - // Patching the repo to get deterministic timestamp - patch_openssl(out_dir, "det_timestamp.patch").unwrap(); - } - let perl_program = env::var("OPENSSL_SRC_PERL").unwrap_or(env::var("PERL").unwrap_or("perl".to_string())); let mut configure = Command::new(perl_program);