summaryrefslogtreecommitdiffstats
path: root/third_party/rust/nss-gk-api
diff options
context:
space:
mode:
Diffstat (limited to 'third_party/rust/nss-gk-api')
-rw-r--r--third_party/rust/nss-gk-api/.cargo-checksum.json1
-rw-r--r--third_party/rust/nss-gk-api/Cargo.toml54
-rw-r--r--third_party/rust/nss-gk-api/README.md6
-rw-r--r--third_party/rust/nss-gk-api/bindings/bindings.toml280
-rw-r--r--third_party/rust/nss-gk-api/bindings/mozpkix.hpp1
-rw-r--r--third_party/rust/nss-gk-api/bindings/nspr_err.h7
-rw-r--r--third_party/rust/nss-gk-api/bindings/nspr_error.h7
-rw-r--r--third_party/rust/nss-gk-api/bindings/nspr_io.h7
-rw-r--r--third_party/rust/nss-gk-api/bindings/nspr_time.h7
-rw-r--r--third_party/rust/nss-gk-api/bindings/nspr_types.h7
-rw-r--r--third_party/rust/nss-gk-api/bindings/nss_ciphers.h8
-rw-r--r--third_party/rust/nss-gk-api/bindings/nss_init.h8
-rw-r--r--third_party/rust/nss-gk-api/bindings/nss_p11.h9
-rw-r--r--third_party/rust/nss-gk-api/bindings/nss_prelude.h8
-rw-r--r--third_party/rust/nss-gk-api/bindings/nss_secerr.h7
-rw-r--r--third_party/rust/nss-gk-api/bindings/nss_ssl.h9
-rw-r--r--third_party/rust/nss-gk-api/bindings/nss_sslerr.h7
-rw-r--r--third_party/rust/nss-gk-api/bindings/nss_sslopt.h7
-rw-r--r--third_party/rust/nss-gk-api/build.rs445
-rw-r--r--third_party/rust/nss-gk-api/src/err.rs257
-rw-r--r--third_party/rust/nss-gk-api/src/exp.rs25
-rw-r--r--third_party/rust/nss-gk-api/src/lib.rs173
-rw-r--r--third_party/rust/nss-gk-api/src/p11.rs194
-rw-r--r--third_party/rust/nss-gk-api/src/prio.rs21
-rw-r--r--third_party/rust/nss-gk-api/src/prtypes.rs22
-rw-r--r--third_party/rust/nss-gk-api/src/ssl.rs157
-rw-r--r--third_party/rust/nss-gk-api/src/time.rs255
-rw-r--r--third_party/rust/nss-gk-api/src/util.rs246
28 files changed, 2235 insertions, 0 deletions
diff --git a/third_party/rust/nss-gk-api/.cargo-checksum.json b/third_party/rust/nss-gk-api/.cargo-checksum.json
new file mode 100644
index 0000000000..43178d2668
--- /dev/null
+++ b/third_party/rust/nss-gk-api/.cargo-checksum.json
@@ -0,0 +1 @@
+{"files":{"Cargo.toml":"484e9876017a5f297ed6921c5336e65235b1e4783753f4f3f31e0256e86ef266","README.md":"a76b467337dd5c5ac8f0e315b1e1f584f2d8c3b6fe57aba16306a3faf9f8fde7","bindings/bindings.toml":"4fbb1e31f56f068e040842408e0e9a5f0f1f35651298afa86836765049a2928b","bindings/mozpkix.hpp":"77072c8bb0f6eb6bfe8cbadc111dcd92e0c79936d13f2e501aae1e5d289a6675","bindings/nspr_err.h":"2d5205d017b536c2d838bcf9bc4ec79f96dd50e7bb9b73892328781f1ee6629d","bindings/nspr_error.h":"e41c03c77b8c22046f8618832c9569fbcc7b26d8b9bbc35eea7168f35e346889","bindings/nspr_io.h":"085b289849ef0e77f88512a27b4d9bdc28252bd4d39c6a17303204e46ef45f72","bindings/nspr_time.h":"2e637fd338a5cf0fd3fb0070a47f474a34c2a7f4447f31b6875f5a9928d0a261","bindings/nspr_types.h":"6e1ca8e760c913e08507dcb8645748661c81b649ac148ee046d2e4e95f39cede","bindings/nss_ciphers.h":"95ec6344a607558b3c5ba8510f463b6295f3a2fb3f538a01410531045a5f62d1","bindings/nss_init.h":"ef49045063782fb612aff459172cc6a89340f15005808608ade5320ca9974310","bindings/nss_p11.h":"0b81e64fe6db49b2ecff94edd850be111ef99ec11220e88ceb1c67be90143a78","bindings/nss_prelude.h":"3952a566aaa497b4c3094bc6c338bba987134a2d5f336c409d1bd6d31e14a8f8","bindings/nss_secerr.h":"713e8368bdae5159af7893cfa517dabfe5103cede051dee9c9557c850a2defc6","bindings/nss_ssl.h":"af222fb957b989e392e762fa2125c82608a0053aff4fb97e556691646c88c335","bindings/nss_sslerr.h":"24b97f092183d8486f774cdaef5030d0249221c78343570d83a4ee5b594210ae","bindings/nss_sslopt.h":"b7807eb7abdad14db6ad7bc51048a46b065a0ea65a4508c95a12ce90e59d1eea","build.rs":"3d8a2d22ca23653d6421a7bb2752c6c532e4cf823b2b3a6622730fd46586665d","src/err.rs":"6c7e785f9c52e7606bc4de3fa4e6805b73cf828430850fd6c4e87bff20f4530e","src/exp.rs":"01e999dc4be93a12e5890b2d8c4ce62d3f5afecf9f8373f150353633731445f9","src/lib.rs":"7b50ec7ddb61cdef461449e4380402569e4ffde7940341dbafc6b60922482132","src/p11.rs":"f79fbc3b639fe6ae7a2c68e2f89de0be9c40b44adcdaba1631fd980c24de77e6","src/prio.rs":"bdfef0e3898876ee223218aedc0b2d2f43575e4302ffdd0fa5a719d4f6e468e0","src/prtypes.rs":"270effec36a2d6836329e672e8043bf277f48fe136c8844f5eb503c0e32511f9","src/ssl.rs":"a36251e63484e382ff70d8c1008ec456746c7826e4c628fe42818880ecf1596b","src/time.rs":"1fce901a535d67baaef59c42f39c5d8eb5a4ac6cbb69026fbc7088dd822fa404","src/util.rs":"f3c163ff609b4211e1ebab08604f770e4bcf11a6946c9d0ecb4165594a4bb886"},"package":"1a689b62ba71fda80458a77b6ace9371d6e6a5473300901383ebd101659b3352"} \ No newline at end of file
diff --git a/third_party/rust/nss-gk-api/Cargo.toml b/third_party/rust/nss-gk-api/Cargo.toml
new file mode 100644
index 0000000000..0993117459
--- /dev/null
+++ b/third_party/rust/nss-gk-api/Cargo.toml
@@ -0,0 +1,54 @@
+# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO
+#
+# When uploading crates to the registry Cargo will automatically
+# "normalize" Cargo.toml files for maximal compatibility
+# with all versions of Cargo and also rewrite `path` dependencies
+# to registry (e.g., crates.io) dependencies.
+#
+# If you are reading this file be aware that the original Cargo.toml
+# will likely look very different (and much more reasonable).
+# See Cargo.toml.orig for the original contents.
+
+[package]
+edition = "2018"
+rust-version = "1.57.0"
+name = "nss-gk-api"
+version = "0.2.1"
+authors = [
+ "Martin Thomson <mt@lowentropy.net>",
+ "Andy Leiserson <aleiserson@mozilla.com>",
+ "John M. Schanck <jschanck@mozilla.com>",
+]
+description = "Gecko API for NSS"
+readme = "README.md"
+license = "MIT/Apache-2.0"
+repository = "https://github.com/mozilla/nss-gk-api"
+
+[dependencies.once_cell]
+version = "1"
+
+[dependencies.pkcs11-bindings]
+version = ">= 0.1.3"
+
+[build-dependencies.bindgen]
+version = ">= 0.59.2"
+features = ["runtime"]
+default-features = false
+
+[build-dependencies.mozbuild]
+version = "0.1"
+optional = true
+
+[build-dependencies.serde]
+version = "1.0"
+
+[build-dependencies.serde_derive]
+version = "1.0"
+
+[build-dependencies.toml]
+version = "0.5"
+
+[features]
+default = ["deny-warnings"]
+deny-warnings = []
+gecko = ["mozbuild"]
diff --git a/third_party/rust/nss-gk-api/README.md b/third_party/rust/nss-gk-api/README.md
new file mode 100644
index 0000000000..f86711a931
--- /dev/null
+++ b/third_party/rust/nss-gk-api/README.md
@@ -0,0 +1,6 @@
+# (UNSTABLE) Gecko API for NSS
+
+nss-gk-api is intended to provide a safe and idiomatic Rust interface to NSS. It is based on code from neqo-crypto, but has been factored out of mozilla-central so that it can be used in standalone applications and libraries such as authenticator-rs. That said, it is *primarily* for use in Gecko, and will not be extended to support arbitrary use cases.
+
+This is work in progress and major changes are expected. In particular, a new version of the `nss-sys` crate will be factored out of this crate.
+
diff --git a/third_party/rust/nss-gk-api/bindings/bindings.toml b/third_party/rust/nss-gk-api/bindings/bindings.toml
new file mode 100644
index 0000000000..15c640765c
--- /dev/null
+++ b/third_party/rust/nss-gk-api/bindings/bindings.toml
@@ -0,0 +1,280 @@
+# In this file, every section corresponds to a header file.
+# A corresponding binding file will be created in $OUT_DIR.
+#
+# This configuration is processed by build.rs to populate `exclude` for each
+# header with the types/variables/functions/enums declared for generation in
+# other headers.
+
+[nspr_types]
+types = [
+ "PRBool",
+ "PRIntn",
+ "PRInt16",
+ "PRInt32",
+ "PRInt64",
+ "PROffset32",
+ "PROffset64",
+ "PRSize",
+ "PRStatus",
+ "PRUintn",
+ "PRUint8",
+ "PRUint16",
+ "PRUint32",
+ "PRUint64",
+ "PRUword",
+]
+variables = [
+ "PR_FALSE",
+ "PR_TRUE",
+]
+
+[nss_prelude]
+types = [
+ "SECItem",
+ "SECItemArray",
+ "SECItemStr",
+ "SECStatus",
+]
+functions = [
+ "SECITEM_FreeItem",
+]
+enums = [
+ "_SECStatus",
+ "SECItemType",
+]
+
+[nss_ssl]
+types = [
+ "SSLAlertDescription",
+ "SSLExtensionType",
+ "SSLNamedGroup",
+ "SSLProtocolVariant",
+ "SSLSignatureScheme",
+]
+functions = [
+ "SSL_AlertSentCallback",
+ "SSL_AuthCertificateComplete",
+ "SSL_AuthCertificateHook",
+ "SSL_CipherPrefSet",
+ "SSL_ConfigServerCert",
+ "SSL_ConfigServerSessionIDCache",
+ "SSL_DestroyResumptionTokenInfo",
+ "SSL_GetChannelInfo",
+ "SSL_GetImplementedCiphers",
+ "SSL_GetNextProto",
+ "SSL_GetNumImplementedCiphers",
+ "SSL_GetPreliminaryChannelInfo",
+ "SSL_GetResumptionTokenInfo",
+ "SSL_ForceHandshake",
+ "SSL_ImportFD",
+ "SSL_NamedGroupConfig",
+ "SSL_OptionSet",
+ "SSL_OptionGetDefault",
+ "SSL_PeerCertificate",
+ "SSL_PeerCertificateChain",
+ "SSL_PeerSignedCertTimestamps",
+ "SSL_PeerStapledOCSPResponses",
+ "SSL_ResetHandshake",
+ "SSL_SetNextProtoNego",
+ "SSL_SetURL",
+ "SSL_VersionRangeSet",
+]
+enums = [
+ "SSLAuthType",
+ "SSLCipherAlgorithm",
+ "SSLCompressionMethod",
+ "SSLExtensionType",
+ "SSLKEAType",
+ "SSLMACAlgorithm",
+ "SSLNamedGroup",
+ "SSLNextProtoState",
+ "SSLProtocolVariant",
+ "SSLSignatureScheme",
+]
+variables = [
+ "SSL_LIBRARY_VERSION_TLS_\\d_\\d",
+ "SSL_NumImplementedCiphers",
+ "ssl_preinfo_.*",
+]
+opaque = [
+ "SSLExtraServerCertData",
+]
+
+[nss_sslopt]
+variables = [
+ "SSL_REQUEST_CERTIFICATE",
+ "SSL_REQUIRE_CERTIFICATE",
+ "SSL_NO_LOCKS",
+ "SSL_ENABLE_SESSION_TICKETS",
+ "SSL_ENABLE_OCSP_STAPLING",
+ "SSL_ENABLE_ALPN",
+ "SSL_ENABLE_EXTENDED_MASTER_SECRET",
+ "SSL_ENABLE_SIGNED_CERT_TIMESTAMPS",
+ "SSL_ENABLE_0RTT_DATA",
+ "SSL_RECORD_SIZE_LIMIT",
+ "SSL_ENABLE_TLS13_COMPAT_MODE",
+ "SSL_ENABLE_HELLO_DOWNGRADE_CHECK",
+ "SSL_SUPPRESS_END_OF_EARLY_DATA",
+]
+
+[nss_ciphers]
+variables = ["TLS_.*"]
+exclude = [
+ ".*_(?:EXPORT(?:1024)?|anon|DES|RC4)_.*",
+ ".*_(?:MD5|NULL_SHA)",
+]
+
+[nss_secerr]
+types = ["SECErrorCodes"]
+enums = ["SECErrorCodes"]
+
+[nss_sslerr]
+types = ["SSLErrorCodes"]
+enums = ["SSLErrorCodes"]
+
+[nss_init]
+functions = [
+ "NSS_Initialize",
+ "NSS_IsInitialized",
+ "NSS_NoDB_Init",
+ "NSS_SetDomesticPolicy",
+ "NSS_Shutdown",
+ "NSS_VersionCheck",
+]
+variables = [
+ "NSS_INIT_READONLY",
+ "SECMOD_DB",
+]
+
+[nss_p11]
+types = [
+ "CERTCertList",
+ "CERTCertListNode",
+ "HpkeAeadId",
+ "HpkeKdfId",
+ "HpkeKemId",
+]
+functions = [
+ "CERT_DestroyCertificate",
+ "CERT_DestroyCertList",
+ "CERT_GetCertificateDer",
+ "PK11_CipherOp",
+ "PK11_CreateContextBySymKey",
+ "PK11_Decrypt",
+ "PK11_DestroyContext",
+ "PK11_DigestOp",
+ "PK11_DigestFinal",
+ "PK11_Encrypt",
+ "PK11_ExtractKeyValue",
+ "PK11_FindCertFromNickname",
+ "PK11_FindKeyByAnyCert",
+ "PK11_FreeSlot",
+ "PK11_FreeSymKey",
+ "PK11_GenerateKeyPairWithOpFlags",
+ "PK11_GenerateRandom",
+ "PK11_GetBlockSize",
+ "PK11_GetInternalSlot",
+ "PK11_GetKeyData",
+ "PK11_GetMechanism",
+ "PK11_HashBuf",
+ "PK11_HPKE_Serialize",
+ "PK11_ImportDataKey",
+ "PK11_ImportDERPrivateKeyInfoAndReturnKey",
+ "PK11_ImportSymKey",
+ "PK11_PubDeriveWithKDF",
+ "PK11_ReadRawAttribute",
+ "PK11_ReferenceSymKey",
+ "SECKEY_CopyPrivateKey",
+ "SECKEY_CopyPublicKey",
+ "SECKEY_DecodeDERSubjectPublicKeyInfo",
+ "SECKEY_DestroyPrivateKey",
+ "SECKEY_DestroyPublicKey",
+ "SECKEY_DestroySubjectPublicKeyInfo",
+ "SECKEY_ExtractPublicKey",
+ "SECKEY_ConvertToPublicKey",
+ "SECOID_FindOIDByTag",
+]
+enums = [
+ "HpkeAeadId",
+ "HpkeKdfId",
+ "HpkeKemId",
+ "PK11ObjectType",
+ "PK11Origin",
+ "SECOidTag",
+]
+opaque = [
+ "CERTCertificateStr",
+ "PK11ContextStr",
+ "PK11SlotInfoStr",
+ "PK11SymKeyStr",
+ "SECKEYPrivateKeyStr",
+ "SECKEYPublicKeyStr",
+]
+variables = [
+ "AES_BLOCK_SIZE",
+ "PK11_ATTR_INSENSITIVE",
+ "PK11_ATTR_PRIVATE",
+ "PK11_ATTR_PUBLIC",
+ "PK11_ATTR_SENSITIVE",
+ "PK11_ATTR_SESSION",
+ "SEC_ASN1_OBJECT_ID",
+ "SHA256_LENGTH",
+]
+
+[nspr_err]
+variables = ["PR_.*_ERROR"]
+
+[nspr_error]
+types = [
+ "PRErrorCode",
+]
+functions = [
+ "PR_ErrorToName",
+ "PR_ErrorToString",
+ "PR_GetError",
+ "PR_SetError",
+]
+variables = [
+ "PR_LANGUAGE_I_DEFAULT",
+]
+
+[nspr_io]
+types = [
+ "PRFileDesc",
+ "PRFileInfo",
+ "PRFileInfo64",
+ "PRFilePrivate",
+ "PRIOMethods",
+ "PRIOVec",
+ "PRSeekFN",
+ "PRSeek64FN",
+]
+functions = [
+ "PR_Close",
+ "PR_CreateIOLayerStub",
+ "PR_GetUniqueIdentity",
+]
+variables = [
+ "PR_AF_INET",
+]
+# opaque is for the stuff we don't plan to use, but we need for function signatures.
+opaque = [
+ "PRFileDesc",
+ "PRFileInfo",
+ "PRFileInfo64",
+ "PRFilePrivate",
+ "PRIOVec",
+]
+enums = [
+ "PRDescType",
+ "PRSeekWhence",
+]
+
+[nspr_time]
+types = ["PRTime"]
+functions = ["PR_Now"]
+
+[mozpkix]
+cplusplus = true
+types = ["mozilla::pkix::ErrorCode"]
+enums = ["mozilla::pkix::ErrorCode"]
diff --git a/third_party/rust/nss-gk-api/bindings/mozpkix.hpp b/third_party/rust/nss-gk-api/bindings/mozpkix.hpp
new file mode 100644
index 0000000000..d0a6cb5861
--- /dev/null
+++ b/third_party/rust/nss-gk-api/bindings/mozpkix.hpp
@@ -0,0 +1 @@
+#include "mozpkix/pkixnss.h" \ No newline at end of file
diff --git a/third_party/rust/nss-gk-api/bindings/nspr_err.h b/third_party/rust/nss-gk-api/bindings/nspr_err.h
new file mode 100644
index 0000000000..204e771c49
--- /dev/null
+++ b/third_party/rust/nss-gk-api/bindings/nspr_err.h
@@ -0,0 +1,7 @@
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+#include "prerr.h"
diff --git a/third_party/rust/nss-gk-api/bindings/nspr_error.h b/third_party/rust/nss-gk-api/bindings/nspr_error.h
new file mode 100644
index 0000000000..8ff8ce202d
--- /dev/null
+++ b/third_party/rust/nss-gk-api/bindings/nspr_error.h
@@ -0,0 +1,7 @@
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+#include "prerror.h"
diff --git a/third_party/rust/nss-gk-api/bindings/nspr_io.h b/third_party/rust/nss-gk-api/bindings/nspr_io.h
new file mode 100644
index 0000000000..9997fb812e
--- /dev/null
+++ b/third_party/rust/nss-gk-api/bindings/nspr_io.h
@@ -0,0 +1,7 @@
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+#include "prio.h"
diff --git a/third_party/rust/nss-gk-api/bindings/nspr_time.h b/third_party/rust/nss-gk-api/bindings/nspr_time.h
new file mode 100644
index 0000000000..f5596577fa
--- /dev/null
+++ b/third_party/rust/nss-gk-api/bindings/nspr_time.h
@@ -0,0 +1,7 @@
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+#include "prtime.h"
diff --git a/third_party/rust/nss-gk-api/bindings/nspr_types.h b/third_party/rust/nss-gk-api/bindings/nspr_types.h
new file mode 100644
index 0000000000..624587560b
--- /dev/null
+++ b/third_party/rust/nss-gk-api/bindings/nspr_types.h
@@ -0,0 +1,7 @@
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+#include "prtypes.h"
diff --git a/third_party/rust/nss-gk-api/bindings/nss_ciphers.h b/third_party/rust/nss-gk-api/bindings/nss_ciphers.h
new file mode 100644
index 0000000000..f064f39c5d
--- /dev/null
+++ b/third_party/rust/nss-gk-api/bindings/nss_ciphers.h
@@ -0,0 +1,8 @@
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+#define SSL_DISABLE_DEPRECATED_CIPHER_SUITE_NAMES
+#include "sslproto.h"
diff --git a/third_party/rust/nss-gk-api/bindings/nss_init.h b/third_party/rust/nss-gk-api/bindings/nss_init.h
new file mode 100644
index 0000000000..ae111bac2b
--- /dev/null
+++ b/third_party/rust/nss-gk-api/bindings/nss_init.h
@@ -0,0 +1,8 @@
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+#include "nss.h"
+#include "ssl.h" // For NSS_SetDomesticPolicy
diff --git a/third_party/rust/nss-gk-api/bindings/nss_p11.h b/third_party/rust/nss-gk-api/bindings/nss_p11.h
new file mode 100644
index 0000000000..7de50eebec
--- /dev/null
+++ b/third_party/rust/nss-gk-api/bindings/nss_p11.h
@@ -0,0 +1,9 @@
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+#include "cert.h"
+#include "keyhi.h"
+#include "pk11pub.h"
diff --git a/third_party/rust/nss-gk-api/bindings/nss_prelude.h b/third_party/rust/nss-gk-api/bindings/nss_prelude.h
new file mode 100644
index 0000000000..72af8f03c9
--- /dev/null
+++ b/third_party/rust/nss-gk-api/bindings/nss_prelude.h
@@ -0,0 +1,8 @@
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+#include "seccomon.h"
+#include "secitem.h"
diff --git a/third_party/rust/nss-gk-api/bindings/nss_secerr.h b/third_party/rust/nss-gk-api/bindings/nss_secerr.h
new file mode 100644
index 0000000000..c2b2d4020c
--- /dev/null
+++ b/third_party/rust/nss-gk-api/bindings/nss_secerr.h
@@ -0,0 +1,7 @@
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+#include "secerr.h"
diff --git a/third_party/rust/nss-gk-api/bindings/nss_ssl.h b/third_party/rust/nss-gk-api/bindings/nss_ssl.h
new file mode 100644
index 0000000000..1cde112cf2
--- /dev/null
+++ b/third_party/rust/nss-gk-api/bindings/nss_ssl.h
@@ -0,0 +1,9 @@
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+#include "sslproto.h"
+#include "ssl.h"
+#include "sslexp.h"
diff --git a/third_party/rust/nss-gk-api/bindings/nss_sslerr.h b/third_party/rust/nss-gk-api/bindings/nss_sslerr.h
new file mode 100644
index 0000000000..74a836f1e8
--- /dev/null
+++ b/third_party/rust/nss-gk-api/bindings/nss_sslerr.h
@@ -0,0 +1,7 @@
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+#include "sslerr.h"
diff --git a/third_party/rust/nss-gk-api/bindings/nss_sslopt.h b/third_party/rust/nss-gk-api/bindings/nss_sslopt.h
new file mode 100644
index 0000000000..a14e1e69d1
--- /dev/null
+++ b/third_party/rust/nss-gk-api/bindings/nss_sslopt.h
@@ -0,0 +1,7 @@
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+#include "ssl.h"
diff --git a/third_party/rust/nss-gk-api/build.rs b/third_party/rust/nss-gk-api/build.rs
new file mode 100644
index 0000000000..ab1892936b
--- /dev/null
+++ b/third_party/rust/nss-gk-api/build.rs
@@ -0,0 +1,445 @@
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+#![cfg_attr(feature = "deny-warnings", deny(warnings))]
+#![warn(clippy::pedantic)]
+
+use bindgen::Builder;
+use serde_derive::Deserialize;
+use std::collections::HashMap;
+use std::collections::HashSet;
+use std::env;
+use std::fs;
+use std::path::{Path, PathBuf};
+use std::process::Command;
+
+const BINDINGS_DIR: &str = "bindings";
+const BINDINGS_CONFIG: &str = "bindings.toml";
+
+// This is the format of a single section of the configuration file.
+#[derive(Deserialize)]
+struct Bindings {
+ /// types that are explicitly included
+ #[serde(default)]
+ types: Vec<String>,
+ /// functions that are explicitly included
+ #[serde(default)]
+ functions: Vec<String>,
+ /// variables (and `#define`s) that are explicitly included
+ #[serde(default)]
+ variables: Vec<String>,
+ /// types that should be explicitly marked as opaque
+ #[serde(default)]
+ opaque: Vec<String>,
+ /// enumerations that are turned into a module (without this, the enum is
+ /// mapped using the default, which means that the individual values are
+ /// formed with an underscore as <enum_type>_<enum_value_name>).
+ #[serde(default)]
+ enums: Vec<String>,
+
+ /// Any item that is specifically excluded; if none of the types, functions,
+ /// or variables fields are specified, everything defined will be mapped,
+ /// so this can be used to limit that.
+ #[serde(default)]
+ exclude: Vec<String>,
+
+ /// Whether the file is to be interpreted as C++
+ #[serde(default)]
+ cplusplus: bool,
+}
+
+fn is_debug() -> bool {
+ env::var("DEBUG")
+ .map(|d| d.parse::<bool>().unwrap_or(false))
+ .unwrap_or(false)
+}
+
+// bindgen needs access to libclang.
+// On windows, this doesn't just work, you have to set LIBCLANG_PATH.
+// Rather than download the 400Mb+ files, like gecko does, let's just reuse their work.
+fn setup_clang() {
+ if env::consts::OS != "windows" {
+ return;
+ }
+ println!("rerun-if-env-changed=LIBCLANG_PATH");
+ println!("rerun-if-env-changed=MOZBUILD_STATE_PATH");
+ if env::var("LIBCLANG_PATH").is_ok() {
+ return;
+ }
+ let mozbuild_root = if let Ok(dir) = env::var("MOZBUILD_STATE_PATH") {
+ PathBuf::from(dir.trim())
+ } else {
+ eprintln!("warning: Building without a gecko setup is not likely to work.");
+ eprintln!(" A working libclang is needed to build nss-sys.");
+ eprintln!(" Either LIBCLANG_PATH or MOZBUILD_STATE_PATH needs to be set.");
+ eprintln!();
+ eprintln!(" We recommend checking out https://github.com/mozilla/gecko-dev");
+ eprintln!(" Then run `./mach bootstrap` which will retrieve clang.");
+ eprintln!(" Make sure to export MOZBUILD_STATE_PATH when building.");
+ return;
+ };
+ let libclang_dir = mozbuild_root.join("clang").join("lib");
+ if libclang_dir.is_dir() {
+ env::set_var("LIBCLANG_PATH", libclang_dir.to_str().unwrap());
+ println!("rustc-env:LIBCLANG_PATH={}", libclang_dir.to_str().unwrap());
+ } else {
+ println!("warning: LIBCLANG_PATH isn't set; maybe run ./mach bootstrap with gecko");
+ }
+}
+
+fn nss_dir() -> PathBuf {
+ let dir = if let Ok(dir) = env::var("NSS_DIR") {
+ let path = PathBuf::from(dir.trim());
+ assert!(
+ !path.is_relative(),
+ "The NSS_DIR environment variable is expected to be an absolute path."
+ );
+ path
+ } else {
+ let out_dir = env::var("OUT_DIR").unwrap();
+ let dir = Path::new(&out_dir).join("nss");
+ if !dir.exists() {
+ Command::new("hg")
+ .args(&[
+ "clone",
+ "https://hg.mozilla.org/projects/nss",
+ dir.to_str().unwrap(),
+ ])
+ .status()
+ .expect("can't clone nss");
+ }
+ let nspr_dir = Path::new(&out_dir).join("nspr");
+ if !nspr_dir.exists() {
+ Command::new("hg")
+ .args(&[
+ "clone",
+ "https://hg.mozilla.org/projects/nspr",
+ nspr_dir.to_str().unwrap(),
+ ])
+ .status()
+ .expect("can't clone nspr");
+ }
+ dir
+ };
+ assert!(dir.is_dir(), "NSS_DIR {:?} doesn't exist", dir);
+ // Note that this returns a relative path because UNC
+ // paths on windows cause certain tools to explode.
+ dir
+}
+
+fn get_bash() -> PathBuf {
+ // When running under MOZILLABUILD, we need to make sure not to invoke
+ // another instance of bash that might be sitting around (like WSL).
+ match env::var("MOZILLABUILD") {
+ Ok(d) => PathBuf::from(d).join("msys").join("bin").join("bash.exe"),
+ Err(_) => PathBuf::from("bash"),
+ }
+}
+
+fn build_nss(dir: PathBuf) {
+ let mut build_nss = vec![
+ String::from("./build.sh"),
+ String::from("-Ddisable_tests=1"),
+ ];
+ if is_debug() {
+ build_nss.push(String::from("--static"));
+ } else {
+ build_nss.push(String::from("-o"));
+ }
+ if let Ok(d) = env::var("NSS_JOBS") {
+ build_nss.push(String::from("-j"));
+ build_nss.push(d);
+ }
+ let status = Command::new(get_bash())
+ .args(build_nss)
+ .current_dir(dir)
+ .status()
+ .expect("couldn't start NSS build");
+ assert!(status.success(), "NSS build failed");
+}
+
+fn dynamic_link() {
+ let libs = if env::consts::OS == "windows" {
+ &["nssutil3.dll", "nss3.dll", "ssl3.dll"]
+ } else {
+ &["nssutil3", "nss3", "ssl3"]
+ };
+ dynamic_link_both(libs);
+}
+
+fn dynamic_link_both(extra_libs: &[&str]) {
+ let nspr_libs = if env::consts::OS == "windows" {
+ &["libplds4", "libplc4", "libnspr4"]
+ } else {
+ &["plds4", "plc4", "nspr4"]
+ };
+ for lib in nspr_libs.iter().chain(extra_libs) {
+ println!("cargo:rustc-link-lib=dylib={}", lib);
+ }
+}
+
+fn static_link() {
+ let mut static_libs = vec![
+ "certdb",
+ "certhi",
+ "cryptohi",
+ "freebl",
+ "nss_static",
+ "nssb",
+ "nssdev",
+ "nsspki",
+ "nssutil",
+ "pk11wrap",
+ "pkcs12",
+ "pkcs7",
+ "smime",
+ "softokn_static",
+ "ssl",
+ ];
+ if env::consts::OS != "macos" {
+ static_libs.push("sqlite");
+ }
+ for lib in static_libs {
+ println!("cargo:rustc-link-lib=static={}", lib);
+ }
+
+ // Dynamic libs that aren't transitively included by NSS libs.
+ let mut other_libs = Vec::new();
+ if env::consts::OS != "windows" {
+ other_libs.extend_from_slice(&["pthread", "dl", "c", "z"]);
+ }
+ if env::consts::OS == "macos" {
+ other_libs.push("sqlite3");
+ }
+ dynamic_link_both(&other_libs);
+}
+
+fn get_includes(nsstarget: &Path, nssdist: &Path) -> Vec<PathBuf> {
+ let nsprinclude = nsstarget.join("include").join("nspr");
+ let nssinclude = nssdist.join("public").join("nss");
+ let includes = vec![nsprinclude, nssinclude];
+ for i in &includes {
+ println!("cargo:include={}", i.to_str().unwrap());
+ }
+ includes
+}
+
+fn build_bindings(base: &str, bindings: &Bindings, flags: &[String], gecko: bool) {
+ let suffix = if bindings.cplusplus { ".hpp" } else { ".h" };
+ let header_path = PathBuf::from(BINDINGS_DIR).join(String::from(base) + suffix);
+ let header = header_path.to_str().unwrap();
+ let out = PathBuf::from(env::var("OUT_DIR").unwrap()).join(String::from(base) + ".rs");
+
+ println!("cargo:rerun-if-changed={}", header);
+
+ let mut builder = Builder::default().header(header);
+ builder = builder.generate_comments(false);
+ builder = builder.size_t_is_usize(true);
+
+ builder = builder.clang_arg("-v");
+
+ if !gecko {
+ builder = builder.clang_arg("-DNO_NSPR_10_SUPPORT");
+ if env::consts::OS == "windows" {
+ builder = builder.clang_arg("-DWIN");
+ } else if env::consts::OS == "macos" {
+ builder = builder.clang_arg("-DDARWIN");
+ } else if env::consts::OS == "linux" {
+ builder = builder.clang_arg("-DLINUX");
+ } else if env::consts::OS == "android" {
+ builder = builder.clang_arg("-DLINUX");
+ builder = builder.clang_arg("-DANDROID");
+ }
+ if bindings.cplusplus {
+ builder = builder.clang_args(&["-x", "c++", "-std=c++11"]);
+ }
+ }
+
+ builder = builder.clang_args(flags);
+
+ // Apply the configuration.
+ for v in &bindings.types {
+ builder = builder.allowlist_type(v);
+ }
+ for v in &bindings.functions {
+ builder = builder.allowlist_function(v);
+ }
+ for v in &bindings.variables {
+ builder = builder.allowlist_var(v);
+ }
+ for v in &bindings.exclude {
+ builder = builder.blocklist_item(v);
+ }
+ for v in &bindings.opaque {
+ builder = builder.opaque_type(v);
+ }
+ for v in &bindings.enums {
+ builder = builder.constified_enum_module(v);
+ }
+
+ let bindings = builder.generate().expect("unable to generate bindings");
+ bindings
+ .write_to_file(out)
+ .expect("couldn't write bindings");
+}
+
+fn setup_standalone() -> Vec<String> {
+ setup_clang();
+
+ let nss = nss_dir();
+ println!("cargo:rerun-if-env-changed={}", nss.display());
+
+ build_nss(nss.clone());
+
+ // $NSS_DIR/../dist/
+ let nssdist = nss.parent().unwrap().join("dist");
+ println!("cargo:rerun-if-env-changed={}", nssdist.display());
+
+ let nsstarget = env::var("NSS_TARGET")
+ .unwrap_or_else(|_| fs::read_to_string(nssdist.join("latest")).unwrap());
+ let nsstarget = nssdist.join(nsstarget.trim());
+
+ let includes = get_includes(&nsstarget, &nssdist);
+
+ let nsslibdir = nsstarget.join("lib");
+ println!(
+ "cargo:rustc-link-search=native={}",
+ nsslibdir.to_str().unwrap()
+ );
+ if is_debug() {
+ static_link();
+ } else {
+ dynamic_link();
+ }
+
+ let mut flags: Vec<String> = Vec::new();
+ for i in includes {
+ flags.push(String::from("-I") + i.to_str().unwrap());
+ }
+
+ flags
+}
+
+#[cfg(feature = "gecko")]
+fn setup_for_gecko() -> Vec<String> {
+ use mozbuild::TOPOBJDIR;
+
+ let fold_libs = mozbuild::config::MOZ_FOLD_LIBS;
+ let libs = if fold_libs {
+ vec!["nss3"]
+ } else {
+ vec!["nssutil3", "nss3", "ssl3", "plds4", "plc4", "nspr4"]
+ };
+
+ for lib in &libs {
+ println!("cargo:rustc-link-lib=dylib={}", lib);
+ }
+
+ if fold_libs {
+ println!(
+ "cargo:rustc-link-search=native={}",
+ TOPOBJDIR.join("security").to_str().unwrap()
+ );
+ } else {
+ println!(
+ "cargo:rustc-link-search=native={}",
+ TOPOBJDIR.join("dist").join("bin").to_str().unwrap()
+ );
+ let nsslib_path = TOPOBJDIR.join("security").join("nss").join("lib");
+ println!(
+ "cargo:rustc-link-search=native={}",
+ nsslib_path.join("nss").join("nss_nss3").to_str().unwrap()
+ );
+ println!(
+ "cargo:rustc-link-search=native={}",
+ nsslib_path.join("ssl").join("ssl_ssl3").to_str().unwrap()
+ );
+ println!(
+ "cargo:rustc-link-search=native={}",
+ TOPOBJDIR
+ .join("config")
+ .join("external")
+ .join("nspr")
+ .join("pr")
+ .to_str()
+ .unwrap()
+ );
+ }
+
+ let flags_path = TOPOBJDIR.join("netwerk/socket/neqo/extra-bindgen-flags");
+
+ println!("cargo:rerun-if-changed={}", flags_path.to_str().unwrap());
+ let mut flags = fs::read_to_string(flags_path)
+ .expect("Failed to read extra-bindgen-flags file")
+ .split_whitespace()
+ .map(std::borrow::ToOwned::to_owned)
+ .collect::<Vec<String>>();
+
+ flags.push(String::from("-include"));
+ flags.push(
+ TOPOBJDIR
+ .join("dist")
+ .join("include")
+ .join("mozilla-config.h")
+ .to_str()
+ .unwrap()
+ .to_string(),
+ );
+ flags
+}
+
+#[cfg(not(feature = "gecko"))]
+fn setup_for_gecko() -> Vec<String> {
+ unreachable!()
+}
+
+fn process_config(config: &mut HashMap<String, Bindings>) {
+ let mut excludes = HashMap::new();
+ for header in config.keys().cloned() {
+ // Collect the list of types, functions, and variables configured
+ // for generation in any other configured header, and add it to the list
+ // of items excluded from generation in this header. This ensures that
+ // each item only appears in one bindings module, which prevents some
+ // type conflicts. (However, it does mean that appropriate `use`
+ // declarations must be added for the generated modules.)
+ excludes.insert(
+ header.clone(),
+ config.iter().flat_map(|(h, b)| {
+ if *h != header {
+ vec![
+ &b.types,
+ &b.functions,
+ &b.variables,
+ ]
+ } else {
+ vec![]
+ }.into_iter().flat_map(|v| v.iter()).cloned()
+ }).collect::<HashSet<String>>()
+ );
+ }
+
+ for (header, excludes) in excludes.into_iter() {
+ config.get_mut(&header).expect("key disappeared from config?").exclude.extend(excludes.into_iter());
+ }
+}
+
+fn main() {
+ let flags = if cfg!(feature = "gecko") {
+ setup_for_gecko()
+ } else {
+ setup_standalone()
+ };
+
+ let config_file = PathBuf::from(BINDINGS_DIR).join(BINDINGS_CONFIG);
+ println!("cargo:rerun-if-changed={}", config_file.to_str().unwrap());
+ let config = fs::read_to_string(config_file).expect("unable to read binding configuration");
+ let mut config: HashMap<String, Bindings> = ::toml::from_str(&config).unwrap();
+ process_config(&mut config);
+
+ for (k, v) in &config {
+ build_bindings(k, v, &flags[..], cfg!(feature = "gecko"));
+ }
+}
diff --git a/third_party/rust/nss-gk-api/src/err.rs b/third_party/rust/nss-gk-api/src/err.rs
new file mode 100644
index 0000000000..cc52b51f63
--- /dev/null
+++ b/third_party/rust/nss-gk-api/src/err.rs
@@ -0,0 +1,257 @@
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+#![allow(dead_code)]
+#![allow(clippy::upper_case_acronyms)]
+
+use std::os::raw::c_char;
+use std::str::Utf8Error;
+
+use crate::nss_prelude::*;
+use crate::prtypes::*;
+
+include!(concat!(env!("OUT_DIR"), "/nspr_error.rs"));
+mod codes {
+ #![allow(non_snake_case)]
+ include!(concat!(env!("OUT_DIR"), "/nss_secerr.rs"));
+ include!(concat!(env!("OUT_DIR"), "/nss_sslerr.rs"));
+ include!(concat!(env!("OUT_DIR"), "/mozpkix.rs"));
+}
+pub use codes::mozilla_pkix_ErrorCode as mozpkix;
+pub use codes::SECErrorCodes as sec;
+pub use codes::SSLErrorCodes as ssl;
+pub mod nspr {
+ include!(concat!(env!("OUT_DIR"), "/nspr_err.rs"));
+}
+
+pub type Res<T> = Result<T, Error>;
+
+#[derive(Clone, Debug, PartialEq, PartialOrd, Ord, Eq)]
+pub enum Error {
+ AeadError,
+ CertificateLoading,
+ CipherInitFailure,
+ CreateSslSocket,
+ EchRetry(Vec<u8>),
+ HkdfError,
+ InternalError,
+ IntegerOverflow,
+ InvalidEpoch,
+ MixedHandshakeMethod,
+ NoDataAvailable,
+ NssError {
+ name: String,
+ code: PRErrorCode,
+ desc: String,
+ },
+ OverrunError,
+ SelfEncryptFailure,
+ StringError,
+ TimeTravelError,
+ UnsupportedCipher,
+ UnsupportedVersion,
+}
+
+impl Error {
+ pub(crate) fn last_nss_error() -> Self {
+ Self::from(unsafe { PR_GetError() })
+ }
+}
+
+impl std::error::Error for Error {
+ #[must_use]
+ fn cause(&self) -> Option<&dyn std::error::Error> {
+ None
+ }
+ #[must_use]
+ fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
+ None
+ }
+}
+
+impl std::fmt::Display for Error {
+ fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
+ write!(f, "Error: {:?}", self)
+ }
+}
+
+impl From<std::num::TryFromIntError> for Error {
+ #[must_use]
+ fn from(_: std::num::TryFromIntError) -> Self {
+ Self::IntegerOverflow
+ }
+}
+impl From<std::ffi::NulError> for Error {
+ #[must_use]
+ fn from(_: std::ffi::NulError) -> Self {
+ Self::InternalError
+ }
+}
+impl From<Utf8Error> for Error {
+ fn from(_: Utf8Error) -> Self {
+ Self::StringError
+ }
+}
+impl From<PRErrorCode> for Error {
+ fn from(code: PRErrorCode) -> Self {
+ let name = wrap_str_fn(|| unsafe { PR_ErrorToName(code) }, "UNKNOWN_ERROR");
+ let desc = wrap_str_fn(
+ || unsafe { PR_ErrorToString(code, PR_LANGUAGE_I_DEFAULT) },
+ "...",
+ );
+ Self::NssError { name, code, desc }
+ }
+}
+
+use std::ffi::CStr;
+
+fn wrap_str_fn<F>(f: F, dflt: &str) -> String
+where
+ F: FnOnce() -> *const c_char,
+{
+ unsafe {
+ let p = f();
+ if p.is_null() {
+ return dflt.to_string();
+ }
+ CStr::from_ptr(p).to_string_lossy().into_owned()
+ }
+}
+
+pub fn is_blocked(result: &Res<()>) -> bool {
+ match result {
+ Err(Error::NssError { code, .. }) => *code == nspr::PR_WOULD_BLOCK_ERROR,
+ _ => false,
+ }
+}
+
+pub trait IntoResult
+{
+ /// The `Ok` type for the result.
+ type Ok;
+
+ /// Unsafe in our implementors because they take a pointer and have no way
+ /// to ensure that the pointer is valid. An invalid pointer could cause UB
+ /// in `impl Drop for Scoped`.
+ unsafe fn into_result(self) -> Result<Self::Ok, Error>;
+}
+
+pub unsafe fn into_result<P>(ptr: *mut P) -> Result<*mut P, Error> {
+ if ptr.is_null() {
+ Err(Error::last_nss_error())
+ } else {
+ Ok(ptr)
+ }
+}
+
+// This can be used to implement `IntoResult` for pointer types that do not make
+// sense as smart pointers. For smart pointers use `scoped_ptr!`.
+macro_rules! impl_into_result {
+ ($pointer:ty) => {
+ impl $crate::err::IntoResult for *mut $pointer {
+ type Ok = *mut $pointer;
+
+ unsafe fn into_result(self) -> Result<Self::Ok, $crate::err::Error> {
+ $crate::err::into_result(self)
+ }
+ }
+ }
+}
+
+impl IntoResult for SECStatus {
+ type Ok = ();
+
+ unsafe fn into_result(self) -> Result<(), Error> {
+ if self == SECSuccess {
+ Ok(())
+ } else {
+ Err(Error::last_nss_error())
+ }
+ }
+}
+
+pub fn secstatus_to_res(code: SECStatus) -> Res<()> {
+ // Unsafe in the trait, but this impl should be safe.
+ unsafe { SECStatus::into_result(code) }
+}
+
+#[cfg(test)]
+mod tests {
+ use crate::err::{self, is_blocked, secstatus_to_res, Error, PRErrorCode, PR_SetError};
+ use crate::ssl::{SECFailure, SECSuccess};
+ use test_fixture::fixture_init;
+
+ fn set_error_code(code: PRErrorCode) {
+ // This code doesn't work without initializing NSS first.
+ fixture_init();
+ unsafe {
+ PR_SetError(code, 0);
+ }
+ }
+
+ #[test]
+ fn error_code() {
+ fixture_init();
+ assert_eq!(15 - 0x3000, err::ssl::SSL_ERROR_BAD_MAC_READ);
+ assert_eq!(166 - 0x2000, err::sec::SEC_ERROR_LIBPKIX_INTERNAL);
+ assert_eq!(-5998, err::nspr::PR_WOULD_BLOCK_ERROR);
+ }
+
+ #[test]
+ fn is_ok() {
+ assert!(secstatus_to_res(SECSuccess).is_ok());
+ }
+
+ #[test]
+ fn is_err() {
+ set_error_code(err::ssl::SSL_ERROR_BAD_MAC_READ);
+ let r = secstatus_to_res(SECFailure);
+ assert!(r.is_err());
+ match r.unwrap_err() {
+ Error::NssError { name, code, desc } => {
+ assert_eq!(name, "SSL_ERROR_BAD_MAC_READ");
+ assert_eq!(code, -12273);
+ assert_eq!(
+ desc,
+ "SSL received a record with an incorrect Message Authentication Code."
+ );
+ }
+ _ => unreachable!(),
+ }
+ }
+
+ #[test]
+ fn is_err_zero_code() {
+ set_error_code(0);
+ let r = secstatus_to_res(SECFailure);
+ assert!(r.is_err());
+ match r.unwrap_err() {
+ Error::NssError { name, code, .. } => {
+ assert_eq!(name, "UNKNOWN_ERROR");
+ assert_eq!(code, 0);
+ // Note that we don't test |desc| here because that comes from
+ // strerror(0), which is platform-dependent.
+ }
+ _ => unreachable!(),
+ }
+ }
+
+ #[test]
+ fn blocked() {
+ set_error_code(err::nspr::PR_WOULD_BLOCK_ERROR);
+ let r = secstatus_to_res(SECFailure);
+ assert!(r.is_err());
+ assert!(is_blocked(&r));
+ match r.unwrap_err() {
+ Error::NssError { name, code, desc } => {
+ assert_eq!(name, "PR_WOULD_BLOCK_ERROR");
+ assert_eq!(code, -5998);
+ assert_eq!(desc, "The operation would have blocked");
+ }
+ _ => panic!("bad error type"),
+ }
+ }
+}
diff --git a/third_party/rust/nss-gk-api/src/exp.rs b/third_party/rust/nss-gk-api/src/exp.rs
new file mode 100644
index 0000000000..9f2255c1dc
--- /dev/null
+++ b/third_party/rust/nss-gk-api/src/exp.rs
@@ -0,0 +1,25 @@
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+/* TODO
+macro_rules! experimental_api {
+ ( $n:ident ( $( $a:ident : $t:ty ),* $(,)? ) ) => {
+ #[allow(non_snake_case)]
+ #[allow(clippy::too_many_arguments)]
+ pub(crate) unsafe fn $n ( $( $a : $t ),* ) -> Result<(), crate::err::Error> {
+ const EXP_FUNCTION: &str = stringify!($n);
+ let n = ::std::ffi::CString::new(EXP_FUNCTION)?;
+ let f = crate::ssl::SSL_GetExperimentalAPI(n.as_ptr());
+ if f.is_null() {
+ return Err(crate::err::Error::InternalError);
+ }
+ let f: unsafe extern "C" fn( $( $t ),* ) -> crate::SECStatus = ::std::mem::transmute(f);
+ let rv = f( $( $a ),* );
+ crate::err::secstatus_to_res(rv)
+ }
+ };
+}
+*/
diff --git a/third_party/rust/nss-gk-api/src/lib.rs b/third_party/rust/nss-gk-api/src/lib.rs
new file mode 100644
index 0000000000..1cd0f2a6e8
--- /dev/null
+++ b/third_party/rust/nss-gk-api/src/lib.rs
@@ -0,0 +1,173 @@
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+#![cfg_attr(feature = "deny-warnings", deny(warnings))]
+#![warn(clippy::pedantic)]
+// Bindgen auto generated code
+// won't adhere to the clippy rules below
+#![allow(clippy::module_name_repetitions)]
+#![allow(clippy::unseparated_literal_suffix)]
+#![allow(clippy::used_underscore_binding)]
+
+#[macro_use]
+pub mod err;
+#[macro_use]
+mod exp;
+#[macro_use]
+mod util;
+
+pub mod p11;
+mod prio;
+mod ssl;
+pub mod time;
+
+pub use err::{Error, IntoResult, secstatus_to_res};
+pub use p11::{PrivateKey, PublicKey, SymKey};
+pub use util::*;
+
+use once_cell::sync::OnceCell;
+
+use std::ffi::CString;
+use std::path::{Path, PathBuf};
+use std::ptr::null;
+
+const MINIMUM_NSS_VERSION: &str = "3.74";
+
+#[allow(non_snake_case)]
+#[allow(non_upper_case_globals)]
+pub mod nss_prelude {
+ pub use crate::prtypes::*;
+ pub use _SECStatus::*;
+ include!(concat!(env!("OUT_DIR"), "/nss_prelude.rs"));
+}
+pub use nss_prelude::{SECItem, SECItemArray, SECItemType, SECStatus};
+
+#[allow(non_upper_case_globals, clippy::redundant_static_lifetimes)]
+#[allow(clippy::upper_case_acronyms)]
+#[allow(unknown_lints, clippy::borrow_as_ptr)]
+mod nss {
+ use crate::nss_prelude::*;
+ include!(concat!(env!("OUT_DIR"), "/nss_init.rs"));
+}
+
+pub mod prtypes;
+pub use prtypes::*;
+
+// Shadow these bindgen created values to correct their type.
+pub const PR_FALSE: PRBool = prtypes::PR_FALSE as PRBool;
+pub const PR_TRUE: PRBool = prtypes::PR_TRUE as PRBool;
+
+enum NssLoaded {
+ External,
+ NoDb,
+ Db(Box<Path>),
+}
+
+impl Drop for NssLoaded {
+ fn drop(&mut self) {
+ if !matches!(self, Self::External) {
+ unsafe {
+ secstatus_to_res(nss::NSS_Shutdown()).expect("NSS Shutdown failed");
+ }
+ }
+ }
+}
+
+static INITIALIZED: OnceCell<NssLoaded> = OnceCell::new();
+
+fn already_initialized() -> bool {
+ unsafe { nss::NSS_IsInitialized() != 0 }
+}
+
+fn version_check() {
+ let min_ver = CString::new(MINIMUM_NSS_VERSION).unwrap();
+ assert_ne!(
+ unsafe { nss::NSS_VersionCheck(min_ver.as_ptr()) },
+ 0,
+ "Minimum NSS version of {} not supported",
+ MINIMUM_NSS_VERSION,
+ );
+}
+
+/// Initialize NSS. This only executes the initialization routines once, so if there is any chance that
+pub fn init() {
+ // Set time zero.
+ time::init();
+ INITIALIZED.get_or_init(|| {
+ unsafe {
+ version_check();
+ if already_initialized() {
+ return NssLoaded::External;
+ }
+
+ secstatus_to_res(nss::NSS_NoDB_Init(null())).expect("NSS_NoDB_Init failed");
+ secstatus_to_res(nss::NSS_SetDomesticPolicy()).expect("NSS_SetDomesticPolicy failed");
+
+ NssLoaded::NoDb
+ }
+ });
+}
+
+/// This enables SSLTRACE by calling a simple, harmless function to trigger its
+/// side effects. SSLTRACE is not enabled in NSS until a socket is made or
+/// global options are accessed. Reading an option is the least impact approach.
+/// This allows us to use SSLTRACE in all of our unit tests and programs.
+#[cfg(debug_assertions)]
+fn enable_ssl_trace() {
+ let opt = ssl::Opt::Locking.as_int();
+ let mut v: ::std::os::raw::c_int = 0;
+ secstatus_to_res(unsafe { ssl::SSL_OptionGetDefault(opt, &mut v) })
+ .expect("SSL_OptionGetDefault failed");
+}
+
+/// Initialize with a database.
+/// # Panics
+/// If NSS cannot be initialized.
+pub fn init_db<P: Into<PathBuf>>(dir: P) {
+ time::init();
+ INITIALIZED.get_or_init(|| {
+ unsafe {
+ version_check();
+ if already_initialized() {
+ return NssLoaded::External;
+ }
+
+ let path = dir.into();
+ assert!(path.is_dir());
+ let pathstr = path.to_str().expect("path converts to string").to_string();
+ let dircstr = CString::new(pathstr).unwrap();
+ let empty = CString::new("").unwrap();
+ secstatus_to_res(nss::NSS_Initialize(
+ dircstr.as_ptr(),
+ empty.as_ptr(),
+ empty.as_ptr(),
+ nss::SECMOD_DB.as_ptr().cast(),
+ nss::NSS_INIT_READONLY,
+ ))
+ .expect("NSS_Initialize failed");
+
+ secstatus_to_res(nss::NSS_SetDomesticPolicy()).expect("NSS_SetDomesticPolicy failed");
+ secstatus_to_res(ssl::SSL_ConfigServerSessionIDCache(
+ 1024,
+ 0,
+ 0,
+ dircstr.as_ptr(),
+ ))
+ .expect("SSL_ConfigServerSessionIDCache failed");
+
+ #[cfg(debug_assertions)]
+ enable_ssl_trace();
+
+ NssLoaded::Db(path.into_boxed_path())
+ }
+ });
+}
+
+/// # Panics
+/// If NSS isn't initialized.
+pub fn assert_initialized() {
+ INITIALIZED.get().expect("NSS not initialized with init or init_db");
+}
diff --git a/third_party/rust/nss-gk-api/src/p11.rs b/third_party/rust/nss-gk-api/src/p11.rs
new file mode 100644
index 0000000000..5eb6ea040c
--- /dev/null
+++ b/third_party/rust/nss-gk-api/src/p11.rs
@@ -0,0 +1,194 @@
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+#![allow(dead_code)]
+#![allow(non_upper_case_globals)]
+#![allow(non_camel_case_types)]
+#![allow(non_snake_case)]
+
+use crate::err::{secstatus_to_res, Error, Res};
+use crate::util::SECItemMut;
+
+use pkcs11_bindings::CKA_VALUE;
+
+use std::convert::TryFrom;
+use std::os::raw::{c_int, c_uint};
+
+#[must_use]
+pub fn hex_with_len(buf: impl AsRef<[u8]>) -> String {
+ use std::fmt::Write;
+ let buf = buf.as_ref();
+ let mut ret = String::with_capacity(10 + buf.len() * 2);
+ write!(&mut ret, "[{}]: ", buf.len()).unwrap();
+ for b in buf {
+ write!(&mut ret, "{:02x}", b).unwrap();
+ }
+ ret
+}
+
+#[allow(clippy::upper_case_acronyms)]
+#[allow(clippy::unreadable_literal)]
+#[allow(unknown_lints, clippy::borrow_as_ptr)]
+mod nss_p11 {
+ use crate::prtypes::*;
+ use crate::nss_prelude::*;
+ include!(concat!(env!("OUT_DIR"), "/nss_p11.rs"));
+}
+
+use crate::prtypes::*;
+pub use nss_p11::*;
+
+// Shadow these bindgen created values to correct their type.
+pub const SHA256_LENGTH: usize = nss_p11::SHA256_LENGTH as usize;
+pub const AES_BLOCK_SIZE: usize = nss_p11::AES_BLOCK_SIZE as usize;
+
+scoped_ptr!(Certificate, CERTCertificate, CERT_DestroyCertificate);
+scoped_ptr!(CertList, CERTCertList, CERT_DestroyCertList);
+
+scoped_ptr!(SubjectPublicKeyInfo, CERTSubjectPublicKeyInfo, SECKEY_DestroySubjectPublicKeyInfo);
+
+scoped_ptr!(PublicKey, SECKEYPublicKey, SECKEY_DestroyPublicKey);
+impl_clone!(PublicKey, SECKEY_CopyPublicKey);
+
+impl PublicKey {
+ /// Get the HPKE serialization of the public key.
+ ///
+ /// # Errors
+ /// When the key cannot be exported, which can be because the type is not supported.
+ /// # Panics
+ /// When keys are too large to fit in `c_uint/usize`. So only on programming error.
+ pub fn key_data(&self) -> Res<Vec<u8>> {
+ let mut buf = vec![0; 100];
+ let mut len: c_uint = 0;
+ secstatus_to_res(unsafe {
+ PK11_HPKE_Serialize(
+ **self,
+ buf.as_mut_ptr(),
+ &mut len,
+ c_uint::try_from(buf.len()).unwrap(),
+ )
+ })?;
+ buf.truncate(usize::try_from(len).unwrap());
+ Ok(buf)
+ }
+}
+
+impl std::fmt::Debug for PublicKey {
+ fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
+ if let Ok(b) = self.key_data() {
+ write!(f, "PublicKey {}", hex_with_len(b))
+ } else {
+ write!(f, "Opaque PublicKey")
+ }
+ }
+}
+
+scoped_ptr!(PrivateKey, SECKEYPrivateKey, SECKEY_DestroyPrivateKey);
+impl_clone!(PrivateKey, SECKEY_CopyPrivateKey);
+
+impl PrivateKey {
+ /// Get the bits of the private key.
+ ///
+ /// # Errors
+ /// When the key cannot be exported, which can be because the type is not supported
+ /// or because the key data cannot be extracted from the PKCS#11 module.
+ /// # Panics
+ /// When the values are too large to fit. So never.
+ pub fn key_data(&self) -> Res<Vec<u8>> {
+ let mut key_item = SECItemMut::make_empty();
+ secstatus_to_res(unsafe {
+ PK11_ReadRawAttribute(
+ PK11ObjectType::PK11_TypePrivKey,
+ (**self).cast(),
+ CKA_VALUE,
+ key_item.as_mut(),
+ )
+ })?;
+ Ok(key_item.as_slice().to_owned())
+ }
+}
+
+impl std::fmt::Debug for PrivateKey {
+ fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
+ if let Ok(b) = self.key_data() {
+ write!(f, "PrivateKey {}", hex_with_len(b))
+ } else {
+ write!(f, "Opaque PrivateKey")
+ }
+ }
+}
+
+scoped_ptr!(Slot, PK11SlotInfo, PK11_FreeSlot);
+
+impl Slot {
+ pub fn internal() -> Res<Self> {
+ unsafe { Slot::from_ptr(PK11_GetInternalSlot()) }
+ }
+}
+
+// Note: PK11SymKey is internally reference counted
+scoped_ptr!(SymKey, PK11SymKey, PK11_FreeSymKey);
+impl_clone!(SymKey, PK11_ReferenceSymKey);
+
+impl SymKey {
+ /// You really don't want to use this.
+ ///
+ /// # Errors
+ /// Internal errors in case of failures in NSS.
+ pub fn as_bytes(&self) -> Res<&[u8]> {
+ secstatus_to_res(unsafe { PK11_ExtractKeyValue(**self) })?;
+
+ let key_item = unsafe { PK11_GetKeyData(**self) };
+ // This is accessing a value attached to the key, so we can treat this as a borrow.
+ match unsafe { key_item.as_mut() } {
+ None => Err(Error::InternalError),
+ Some(key) => Ok(unsafe { std::slice::from_raw_parts(key.data, key.len as usize) }),
+ }
+ }
+}
+
+impl std::fmt::Debug for SymKey {
+ fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
+ if let Ok(b) = self.as_bytes() {
+ write!(f, "SymKey {}", hex_with_len(b))
+ } else {
+ write!(f, "Opaque SymKey")
+ }
+ }
+}
+
+unsafe fn destroy_pk11_context(ctxt: *mut PK11Context) {
+ PK11_DestroyContext(ctxt, PRBool::from(true));
+}
+scoped_ptr!(Context, PK11Context, destroy_pk11_context);
+
+/// Generate a randomized buffer.
+/// # Panics
+/// When `size` is too large or NSS fails.
+#[must_use]
+pub fn random(size: usize) -> Vec<u8> {
+ let mut buf = vec![0; size];
+ secstatus_to_res(unsafe {
+ PK11_GenerateRandom(buf.as_mut_ptr(), c_int::try_from(buf.len()).unwrap())
+ })
+ .unwrap();
+ buf
+}
+
+impl_into_result!(SECOidData);
+
+#[cfg(test)]
+mod test {
+ use super::random;
+ use test_fixture::fixture_init;
+
+ #[test]
+ fn randomness() {
+ fixture_init();
+ // If this ever fails, there is either a bug, or it's time to buy a lottery ticket.
+ assert_ne!(random(16), random(16));
+ }
+}
diff --git a/third_party/rust/nss-gk-api/src/prio.rs b/third_party/rust/nss-gk-api/src/prio.rs
new file mode 100644
index 0000000000..8468e08eb3
--- /dev/null
+++ b/third_party/rust/nss-gk-api/src/prio.rs
@@ -0,0 +1,21 @@
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+#![allow(clippy::upper_case_acronyms)]
+#![allow(
+ dead_code,
+ non_upper_case_globals,
+ non_snake_case,
+ clippy::cognitive_complexity,
+ clippy::empty_enum,
+ clippy::too_many_lines,
+ unknown_lints,
+ clippy::borrow_as_ptr
+)]
+
+use crate::prtypes::*;
+
+include!(concat!(env!("OUT_DIR"), "/nspr_io.rs"));
diff --git a/third_party/rust/nss-gk-api/src/prtypes.rs b/third_party/rust/nss-gk-api/src/prtypes.rs
new file mode 100644
index 0000000000..2416ec5ddc
--- /dev/null
+++ b/third_party/rust/nss-gk-api/src/prtypes.rs
@@ -0,0 +1,22 @@
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+#![allow(clippy::upper_case_acronyms)]
+#![allow(
+ dead_code,
+ non_upper_case_globals,
+ non_snake_case,
+ clippy::cognitive_complexity,
+ clippy::empty_enum,
+ clippy::too_many_lines,
+ unknown_lints,
+ clippy::borrow_as_ptr
+)]
+
+pub use PRStatus_PR_FAILURE as PR_FAILURE;
+pub use PRStatus_PR_SUCCESS as PR_SUCCESS;
+
+include!(concat!(env!("OUT_DIR"), "/nspr_types.rs"));
diff --git a/third_party/rust/nss-gk-api/src/ssl.rs b/third_party/rust/nss-gk-api/src/ssl.rs
new file mode 100644
index 0000000000..1d74154bc7
--- /dev/null
+++ b/third_party/rust/nss-gk-api/src/ssl.rs
@@ -0,0 +1,157 @@
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+#![allow(
+ dead_code,
+ non_camel_case_types,
+ non_upper_case_globals,
+ non_snake_case,
+ clippy::cognitive_complexity,
+ clippy::too_many_lines,
+ clippy::upper_case_acronyms,
+ unknown_lints,
+ clippy::borrow_as_ptr
+)]
+
+use crate::err::{secstatus_to_res, Res};
+use crate::nss_prelude::*;
+use crate::prio::PRFileDesc;
+
+mod nss_ssl {
+ use crate::err::PRErrorCode;
+ use crate::nss_prelude::*;
+ use crate::p11::CERTCertList;
+ use crate::prio::{
+ PRFileDesc,
+ PRFileInfo,
+ PRFileInfo64,
+ PRIOVec,
+ };
+
+ include!(concat!(env!("OUT_DIR"), "/nss_ssl.rs"));
+}
+pub use nss_ssl::*;
+
+mod SSLOption {
+ include!(concat!(env!("OUT_DIR"), "/nss_sslopt.rs"));
+}
+
+#[derive(Debug, Copy, Clone)]
+pub enum Opt {
+ Locking,
+ Tickets,
+ OcspStapling,
+ Alpn,
+ ExtendedMasterSecret,
+ SignedCertificateTimestamps,
+ EarlyData,
+ RecordSizeLimit,
+ Tls13CompatMode,
+ HelloDowngradeCheck,
+ SuppressEndOfEarlyData,
+}
+
+impl Opt {
+ // Cast is safe here because SSLOptions are within the i32 range
+ #[allow(clippy::cast_possible_wrap)]
+ pub(crate) fn as_int(self) -> PRInt32 {
+ let i = match self {
+ Self::Locking => SSLOption::SSL_NO_LOCKS,
+ Self::Tickets => SSLOption::SSL_ENABLE_SESSION_TICKETS,
+ Self::OcspStapling => SSLOption::SSL_ENABLE_OCSP_STAPLING,
+ Self::Alpn => SSLOption::SSL_ENABLE_ALPN,
+ Self::ExtendedMasterSecret => SSLOption::SSL_ENABLE_EXTENDED_MASTER_SECRET,
+ Self::SignedCertificateTimestamps => SSLOption::SSL_ENABLE_SIGNED_CERT_TIMESTAMPS,
+ Self::EarlyData => SSLOption::SSL_ENABLE_0RTT_DATA,
+ Self::RecordSizeLimit => SSLOption::SSL_RECORD_SIZE_LIMIT,
+ Self::Tls13CompatMode => SSLOption::SSL_ENABLE_TLS13_COMPAT_MODE,
+ Self::HelloDowngradeCheck => SSLOption::SSL_ENABLE_HELLO_DOWNGRADE_CHECK,
+ Self::SuppressEndOfEarlyData => SSLOption::SSL_SUPPRESS_END_OF_EARLY_DATA,
+ };
+ i as PRInt32
+ }
+
+ // Some options are backwards, like SSL_NO_LOCKS, so use this to manage that.
+ fn map_enabled(self, enabled: bool) -> PRIntn {
+ let v = match self {
+ Self::Locking => !enabled,
+ _ => enabled,
+ };
+ PRIntn::from(v)
+ }
+
+ pub(crate) fn set(self, fd: *mut PRFileDesc, value: bool) -> Res<()> {
+ secstatus_to_res(unsafe { SSL_OptionSet(fd, self.as_int(), self.map_enabled(value)) })
+ }
+}
+
+/*
+ * TODO: these will be moved to a dedicated module
+ *
+experimental_api!(SSL_GetCurrentEpoch(
+ fd: *mut PRFileDesc,
+ read_epoch: *mut u16,
+ write_epoch: *mut u16,
+));
+experimental_api!(SSL_HelloRetryRequestCallback(
+ fd: *mut PRFileDesc,
+ cb: SSLHelloRetryRequestCallback,
+ arg: *mut c_void,
+));
+experimental_api!(SSL_RecordLayerWriteCallback(
+ fd: *mut PRFileDesc,
+ cb: SSLRecordWriteCallback,
+ arg: *mut c_void,
+));
+experimental_api!(SSL_RecordLayerData(
+ fd: *mut PRFileDesc,
+ epoch: Epoch,
+ ct: SSLContentType::Type,
+ data: *const u8,
+ len: c_uint,
+));
+experimental_api!(SSL_SendSessionTicket(
+ fd: *mut PRFileDesc,
+ extra: *const u8,
+ len: c_uint,
+));
+experimental_api!(SSL_SetMaxEarlyDataSize(fd: *mut PRFileDesc, size: u32));
+experimental_api!(SSL_SetResumptionToken(
+ fd: *mut PRFileDesc,
+ token: *const u8,
+ len: c_uint,
+));
+experimental_api!(SSL_SetResumptionTokenCallback(
+ fd: *mut PRFileDesc,
+ cb: SSLResumptionTokenCallback,
+ arg: *mut c_void,
+));
+
+experimental_api!(SSL_GetResumptionTokenInfo(
+ token: *const u8,
+ token_len: c_uint,
+ info: *mut SSLResumptionTokenInfo,
+ len: c_uint,
+));
+
+experimental_api!(SSL_DestroyResumptionTokenInfo(
+ info: *mut SSLResumptionTokenInfo,
+));
+*/
+
+#[cfg(test)]
+mod tests {
+ use super::{SSL_GetNumImplementedCiphers, SSL_NumImplementedCiphers};
+
+ #[test]
+ fn num_ciphers() {
+ assert!(unsafe { SSL_NumImplementedCiphers } > 0);
+ assert!(unsafe { SSL_GetNumImplementedCiphers() } > 0);
+ assert_eq!(unsafe { SSL_NumImplementedCiphers }, unsafe {
+ SSL_GetNumImplementedCiphers()
+ });
+ }
+}
diff --git a/third_party/rust/nss-gk-api/src/time.rs b/third_party/rust/nss-gk-api/src/time.rs
new file mode 100644
index 0000000000..1abc276dda
--- /dev/null
+++ b/third_party/rust/nss-gk-api/src/time.rs
@@ -0,0 +1,255 @@
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+#![allow(clippy::upper_case_acronyms)]
+
+use crate::nss_prelude::PRInt64;
+use crate::err::{Error, Res};
+//use crate::prio::PRFileDesc;
+//use crate::ssl::SSLTimeFunc;
+
+use once_cell::sync::OnceCell;
+//use std::boxed::Box;
+use std::convert::{TryFrom, TryInto};
+use std::ops::Deref;
+//use std::os::raw::c_void;
+//use std::pin::Pin;
+use std::time::{Duration, Instant};
+
+include!(concat!(env!("OUT_DIR"), "/nspr_time.rs"));
+
+/* TODO move to exp module
+experimental_api!(SSL_SetTimeFunc(
+ fd: *mut PRFileDesc,
+ cb: SSLTimeFunc,
+ arg: *mut c_void,
+));
+*/
+
+/// This struct holds the zero time used for converting between `Instant` and `PRTime`.
+#[derive(Debug)]
+struct TimeZero {
+ instant: Instant,
+ prtime: PRTime,
+}
+
+impl TimeZero {
+ /// This function sets a baseline from an instance of `Instant`.
+ /// This allows for the possibility that code that uses these APIs will create
+ /// instances of `Instant` before any of this code is run. If `Instant`s older than
+ /// `BASE_TIME` are used with these conversion functions, they will fail.
+ /// To avoid that, we make sure that this sets the base time using the first value
+ /// it sees if it is in the past. If it is not, then use `Instant::now()` instead.
+ pub fn baseline(t: Instant) -> Self {
+ let now = Instant::now();
+ let prnow = unsafe { PR_Now() };
+
+ if now <= t {
+ // `t` is in the future, just use `now`.
+ Self {
+ instant: now,
+ prtime: prnow,
+ }
+ } else {
+ let elapsed = Interval::from(now.duration_since(now));
+ // An error from these unwrap functions would require
+ // ridiculously long application running time.
+ let prelapsed: PRTime = elapsed.try_into().unwrap();
+ Self {
+ instant: t,
+ prtime: prnow.checked_sub(prelapsed).unwrap(),
+ }
+ }
+ }
+}
+
+static BASE_TIME: OnceCell<TimeZero> = OnceCell::new();
+
+fn get_base() -> &'static TimeZero {
+ BASE_TIME.get_or_init(|| TimeZero {
+ instant: Instant::now(),
+ prtime: unsafe { PR_Now() },
+ })
+}
+
+pub fn init() {
+ let _ = get_base();
+}
+
+/// Time wraps Instant and provides conversion functions into `PRTime`.
+#[derive(Clone, Copy, Debug, PartialEq, Eq)]
+pub struct Time {
+ t: Instant,
+}
+
+impl Deref for Time {
+ type Target = Instant;
+ fn deref(&self) -> &Self::Target {
+ &self.t
+ }
+}
+
+impl From<Instant> for Time {
+ /// Convert from an Instant into a Time.
+ fn from(t: Instant) -> Self {
+ // Call `TimeZero::baseline(t)` so that time zero can be set.
+ BASE_TIME.get_or_init(|| TimeZero::baseline(t));
+ Self { t }
+ }
+}
+
+impl TryFrom<PRTime> for Time {
+ type Error = Error;
+ fn try_from(prtime: PRTime) -> Res<Self> {
+ let base = get_base();
+ if let Some(delta) = prtime.checked_sub(base.prtime) {
+ let d = Duration::from_micros(delta.try_into()?);
+ base.instant
+ .checked_add(d)
+ .map_or(Err(Error::TimeTravelError), |t| Ok(Self { t }))
+ } else {
+ Err(Error::TimeTravelError)
+ }
+ }
+}
+
+impl TryInto<PRTime> for Time {
+ type Error = Error;
+ fn try_into(self) -> Res<PRTime> {
+ let base = get_base();
+ let delta = self
+ .t
+ .checked_duration_since(base.instant)
+ .ok_or(Error::TimeTravelError)?;
+ if let Ok(d) = PRTime::try_from(delta.as_micros()) {
+ d.checked_add(base.prtime).ok_or(Error::TimeTravelError)
+ } else {
+ Err(Error::TimeTravelError)
+ }
+ }
+}
+
+impl From<Time> for Instant {
+ #[must_use]
+ fn from(t: Time) -> Self {
+ t.t
+ }
+}
+
+/// Interval wraps Duration and provides conversion functions into `PRTime`.
+#[derive(Clone, Copy, Debug, PartialEq, Eq)]
+pub struct Interval {
+ d: Duration,
+}
+
+impl Deref for Interval {
+ type Target = Duration;
+ fn deref(&self) -> &Self::Target {
+ &self.d
+ }
+}
+
+impl TryFrom<PRTime> for Interval {
+ type Error = Error;
+ fn try_from(prtime: PRTime) -> Res<Self> {
+ Ok(Self {
+ d: Duration::from_micros(u64::try_from(prtime)?),
+ })
+ }
+}
+
+impl From<Duration> for Interval {
+ fn from(d: Duration) -> Self {
+ Self { d }
+ }
+}
+
+impl TryInto<PRTime> for Interval {
+ type Error = Error;
+ fn try_into(self) -> Res<PRTime> {
+ Ok(PRTime::try_from(self.d.as_micros())?)
+ }
+}
+
+/* TODO: restore, experimental only.
+/// `TimeHolder` maintains a `PRTime` value in a form that is accessible to the TLS stack.
+#[derive(Debug)]
+pub struct TimeHolder {
+ t: Pin<Box<PRTime>>,
+}
+
+impl TimeHolder {
+ unsafe extern "C" fn time_func(arg: *mut c_void) -> PRTime {
+ let p = arg as *const PRTime;
+ *p.as_ref().unwrap()
+ }
+
+ pub fn bind(&mut self, fd: *mut PRFileDesc) -> Res<()> {
+ unsafe { SSL_SetTimeFunc(fd, Some(Self::time_func), &mut *self.t as *mut _ as *mut c_void) }
+ }
+
+ pub fn set(&mut self, t: Instant) -> Res<()> {
+ *self.t = Time::from(t).try_into()?;
+ Ok(())
+ }
+}
+
+impl Default for TimeHolder {
+ fn default() -> Self {
+ TimeHolder { t: Box::pin(0) }
+ }
+}
+*/
+
+#[cfg(test)]
+mod test {
+ use super::{get_base, init, Interval, PRTime, Time};
+ use crate::err::Res;
+ use std::convert::{TryFrom, TryInto};
+ use std::time::{Duration, Instant};
+
+ #[test]
+ fn convert_stable() {
+ init();
+ let now = Time::from(Instant::now());
+ let pr: PRTime = now.try_into().expect("convert to PRTime with truncation");
+ let t2 = Time::try_from(pr).expect("convert to Instant");
+ let pr2: PRTime = t2.try_into().expect("convert to PRTime again");
+ assert_eq!(pr, pr2);
+ let t3 = Time::try_from(pr2).expect("convert to Instant again");
+ assert_eq!(t2, t3);
+ }
+
+ #[test]
+ fn past_time() {
+ init();
+ let base = get_base();
+ assert!(Time::try_from(base.prtime - 1).is_err());
+ }
+
+ #[test]
+ fn negative_time() {
+ init();
+ assert!(Time::try_from(-1).is_err());
+ }
+
+ #[test]
+ fn negative_interval() {
+ init();
+ assert!(Interval::try_from(-1).is_err());
+ }
+
+ #[test]
+ // We allow replace_consts here because
+ // std::u64::max_value() isn't available
+ // in all of our targets
+ fn overflow_interval() {
+ init();
+ let interval = Interval::from(Duration::from_micros(u64::max_value()));
+ let res: Res<PRTime> = interval.try_into();
+ assert!(res.is_err());
+ }
+}
diff --git a/third_party/rust/nss-gk-api/src/util.rs b/third_party/rust/nss-gk-api/src/util.rs
new file mode 100644
index 0000000000..f06542eb45
--- /dev/null
+++ b/third_party/rust/nss-gk-api/src/util.rs
@@ -0,0 +1,246 @@
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+use std::convert::TryFrom;
+use std::marker::PhantomData;
+use std::mem;
+use std::os::raw::c_uint;
+use std::ptr::null_mut;
+
+use crate::prtypes::*;
+use crate::nss_prelude::*;
+
+/// Implement a smart pointer for NSS objects.
+///
+/// Most of the time the pointer is like a `Box`, but there are exceptions (e.g.
+/// PK11SymKey is internally reference counted so its pointer is like an `Arc`.)
+///
+/// Named "scoped" because that is what NSS calls its `unique_ptr` typedefs.
+macro_rules! scoped_ptr {
+ ($name:ident, $target:ty, $dtor:path) => {
+ pub struct $name {
+ ptr: *mut $target,
+ }
+
+ impl $name {
+ /// Create a new instance of `$name` from a pointer.
+ ///
+ /// # Errors
+ /// When passed a null pointer generates an error.
+ pub unsafe fn from_ptr(raw: *mut $target) -> Result<Self, $crate::err::Error> {
+ let ptr = $crate::err::into_result(raw)?;
+ Ok(Self { ptr })
+ }
+ }
+
+ impl $crate::err::IntoResult for *mut $target {
+ type Ok = $name;
+
+ unsafe fn into_result(self) -> Result<Self::Ok, $crate::err::Error> {
+ $name::from_ptr(self)
+ }
+ }
+
+ impl std::ops::Deref for $name {
+ type Target = *mut $target;
+ #[must_use]
+ fn deref(&self) -> &*mut $target {
+ &self.ptr
+ }
+ }
+
+ // Original implements DerefMut, but is that really a good idea?
+
+ impl Drop for $name {
+ fn drop(&mut self) {
+ unsafe { $dtor(self.ptr) };
+ }
+ }
+ }
+}
+
+macro_rules! impl_clone {
+ ($name:ty, $nss_fn:path) => {
+ impl Clone for $name {
+ #[must_use]
+ fn clone(&self) -> Self {
+ let ptr = unsafe { $nss_fn(self.ptr) };
+ assert!(!ptr.is_null());
+ Self { ptr }
+ }
+ }
+ }
+}
+
+impl SECItem {
+ /// Return contents as a slice.
+ ///
+ /// Unsafe due to calling from_raw_parts, or if 'a outlives &self. This
+ /// unsafety is encapsulated by the `as_slice` method of `SECItemBorrowed`
+ /// and `SECItemMut`.
+ ///
+ /// Note that safe code can construct a SECItem pointing to anything. The
+ /// same is not true of the safe wrappers `SECItemMut` and `SECItemBorrowed`
+ /// because their inner SECItem is private.
+ pub unsafe fn as_slice<'a>(&self) -> &'a [u8] {
+ // Sanity check the type, as some types don't count bytes in `Item::len`.
+ assert_eq!(self.type_, SECItemType::siBuffer);
+ // Note: `from_raw_parts` requires non-null `data` even for zero-length
+ // slices.
+ if self.len != 0 {
+ std::slice::from_raw_parts(self.data, usize::try_from(self.len).unwrap())
+ } else {
+ &[]
+ }
+ }
+}
+
+/// An owned SECItem.
+///
+/// The SECItem structure is allocated by Rust. The buffer referenced by the
+/// SECItem is allocated by NSS. `SECITEM_FreeItem` will be called to free the
+/// buffer when the SECItemMut is dropped.
+///
+/// This is used with NSS functions that return a variable amount of data.
+#[repr(transparent)]
+pub struct SECItemMut {
+ inner: SECItem,
+}
+
+impl<'a> Drop for SECItemMut {
+ #[allow(unused_must_use)]
+ fn drop(&mut self) {
+ // FreeItem unconditionally frees the buffer referenced by the SECItem.
+ // If the second argument is true, it also frees the SECItem itself,
+ // which we don't want to do, because rust owns that memory.
+ unsafe { SECITEM_FreeItem(&mut self.inner, PRBool::from(false)) };
+ }
+}
+
+impl AsRef<SECItem> for SECItemMut {
+ fn as_ref(&self) -> &SECItem {
+ &self.inner
+ }
+}
+
+impl AsMut<SECItem> for SECItemMut {
+ fn as_mut(&mut self) -> &mut SECItem {
+ &mut self.inner
+ }
+}
+
+impl SECItemMut {
+ /// Return contents as a slice.
+ pub fn as_slice(&self) -> &[u8] {
+ unsafe { self.inner.as_slice() }
+ }
+
+ /// Make an empty `SECItemMut` for passing as a mutable `*SECItem` argument.
+ pub fn make_empty() -> SECItemMut {
+ SECItemMut {
+ inner: SECItem {
+ type_: SECItemType::siBuffer,
+ data: null_mut(),
+ len: 0,
+ }
+ }
+ }
+}
+
+/// A borrowed SECItem.
+///
+/// The SECItem structure is allocated by Rust. The buffer referenced by the
+/// SECItem may be allocated either by Rust or NSS. The SECItem does not own the
+/// buffer and will not free it when dropped.
+///
+/// This is usually used to pass a reference to some borrowed rust memory to
+/// NSS. It is occasionally used to accept non-owned output data from NSS.
+#[repr(transparent)]
+pub struct SECItemBorrowed<'a> {
+ inner: SECItem,
+ phantom_data: PhantomData<&'a u8>,
+}
+
+impl<'a> AsRef<SECItem> for SECItemBorrowed<'a> {
+ fn as_ref(&self) -> &SECItem {
+ &self.inner
+ }
+}
+
+impl<'a> AsMut<SECItem> for SECItemBorrowed<'a> {
+ /// Get a mutable reference to the underlying SECItem struct.
+ ///
+ /// Note that even if the SECItem struct is mutable, the buffer it
+ /// references may not be. Take care not to pass the mutable
+ /// SECItem to NSS routines that will violate mutability rules.
+ //
+ // TODO: Should we make the danger more obvious, by using a non-trait method
+ // with "unsafe" in the name, or an unsafe method?
+ fn as_mut(&mut self) -> &mut SECItem {
+ &mut self.inner
+ }
+}
+
+impl<'a> SECItemBorrowed<'a> {
+ /// Return contents as a slice.
+ pub fn as_slice(&self) -> &'a [u8] {
+ unsafe { self.inner.as_slice() }
+ }
+
+ /// Create an empty `SECItemBorrowed`.
+ ///
+ /// This can be used (1) to pass an empty item as an argument, and (2) as an
+ /// output parameter when NSS returns a pointer to NSS-owned memory that
+ /// should not be freed when the SECItem is dropped. If the memory should
+ /// be freed when the SECItem is dropped, use SECItemMut.
+ ///
+ /// It is safe to let the caller specify any lifetime here because no
+ /// borrowing is actually taking place. However, if the pointer in the
+ /// returned item is modified, care must be taken that the specified
+ /// lifetime accurately reflects the data referenced by the pointer.
+ pub fn make_empty() -> SECItemBorrowed<'a> {
+ SECItemBorrowed {
+ inner: SECItem {
+ type_: SECItemType::siBuffer,
+ data: null_mut(),
+ len: 0,
+ },
+ phantom_data: PhantomData,
+ }
+ }
+
+ /// Create a `SECItemBorrowed` wrapping a slice.
+ ///
+ /// Creating this object is technically safe, but using it is extremely dangerous.
+ /// Minimally, it can only be passed as a `const SECItem*` argument to functions,
+ /// or those that treat their argument as `const`.
+ pub fn wrap(buf: &'a [u8]) -> SECItemBorrowed<'a> {
+ SECItemBorrowed {
+ inner: SECItem {
+ type_: SECItemType::siBuffer,
+ data: buf.as_ptr() as *mut u8,
+ len: c_uint::try_from(buf.len()).unwrap(),
+ },
+ phantom_data: PhantomData,
+ }
+ }
+
+ /// Create a `SECItemBorrowed` wrapping a struct.
+ ///
+ /// Creating this object is technically safe, but using it is extremely dangerous.
+ /// Minimally, it can only be passed as a `const SECItem*` argument to functions,
+ /// or those that treat their argument as `const`.
+ pub fn wrap_struct<T>(v: &'a T) -> SECItemBorrowed<'a> {
+ SECItemBorrowed {
+ inner: SECItem {
+ type_: SECItemType::siBuffer,
+ data: (v as *const T as *mut T).cast(),
+ len: c_uint::try_from(mem::size_of::<T>()).unwrap(),
+ },
+ phantom_data: PhantomData,
+ }
+ }
+}