private static void TraceCallback(void *userData, UnityTls.unitytls_tlsctx *ctx, byte *traceMessage,
                                          size_t traceMessageLen)
        {
            var message = Encoding.UTF8.GetString(traceMessage, (int)traceMessageLen);

            Console.Write(message);
        }
        private static void CertificateCallback(void *userData, UnityTls.unitytls_tlsctx *ctx, byte *cn, size_t cnLen,
                                                UnityTls.unitytls_x509name *caList, size_t caListLen, UnityTls.unitytls_x509list_ref *chain,
                                                UnityTls.unitytls_key_ref *key, UnityTls.unitytls_errorstate *errorState)
        {
            var handle  = (GCHandle)(IntPtr)userData;
            var context = (UnityTlsContext)handle.Target;

            context.CertificateCallback(ctx, cn, cnLen, caList, caListLen, chain, key, errorState);
        }
        private void CertificateCallback(UnityTls.unitytls_tlsctx *ctx, byte *cn, size_t cnLen,
                                         UnityTls.unitytls_x509name *caList, size_t caListLen, UnityTls.unitytls_x509list_ref *chain,
                                         UnityTls.unitytls_key_ref *key, UnityTls.unitytls_errorstate *errorState)
        {
            try
            {
                if (remoteCertificate == null)
                {
                    throw new TlsException(AlertDescription.InternalError,
                                           "Cannot request client certificate before receiving one from the server.");
                }

                localClientCertificate = SelectClientCertificate(null);

                if (localClientCertificate == null)
                {
                    *chain = new UnityTls.unitytls_x509list_ref
                    {
                        handle = UnityTls.NativeInterface.UNITYTLS_INVALID_HANDLE
                    };
                    *key = new UnityTls.unitytls_key_ref {
                        handle = UnityTls.NativeInterface.UNITYTLS_INVALID_HANDLE
                    };
                }
                else
                {
                    // Need to create native objects for client chain/key. Need to keep them cached.
                    // Make sure we don't have old native objects still around.
                    UnityTls.NativeInterface.unitytls_x509list_free(requestedClientCertChain);
                    UnityTls.NativeInterface.unitytls_key_free(requestedClientKey);

                    ExtractNativeKeyAndChainFromManagedCertificate(localClientCertificate, errorState,
                                                                   out requestedClientCertChain, out requestedClientKey);
                    *chain = UnityTls.NativeInterface.unitytls_x509list_get_ref(requestedClientCertChain, errorState);
                    *key   = UnityTls.NativeInterface.unitytls_key_get_ref(requestedClientKey, errorState);
                }

                Unity.Debug.CheckAndThrow(*errorState, "Failed to retrieve certificates on request.",
                                          AlertDescription.HandshakeFailure);
            }
            catch (Exception ex)
            {
                // handle all exceptions and store them for later since we don't want to let them go through native code.
                UnityTls.NativeInterface.unitytls_errorstate_raise_error(errorState,
                                                                         UnityTls.unitytls_error_code.UNITYTLS_USER_UNKNOWN_ERROR);
                if (lastException == null)
                {
                    lastException = ex;
                }
            }
        }
        public override void Shutdown()
        {
            if (Settings != null && Settings.SendCloseNotify)
            {
                var err = UnityTls.NativeInterface.unitytls_errorstate_create();
                UnityTls.NativeInterface.unitytls_tlsctx_notify_close(tlsContext, &err);
            }

            // Destroy native UnityTls objects
            UnityTls.NativeInterface.unitytls_x509list_free(requestedClientCertChain);
            UnityTls.NativeInterface.unitytls_key_free(requestedClientKey);
            UnityTls.NativeInterface.unitytls_tlsctx_free(tlsContext);
            tlsContext = null;

            hasContext = false;
        }
        public UnityTlsContext(
            MobileAuthenticatedStream parent,
            MonoSslAuthenticationOptions opts)
            : base(parent, opts)
        {
            // Need GCHandle to get a consistent pointer to this instance
            handle = GCHandle.Alloc(this);

            var errorState = UnityTls.NativeInterface.unitytls_errorstate_create();

            // Map selected protocols as best as we can.
            var protocolRange = new UnityTls.unitytls_tlsctx_protocolrange
            {
                min = UnityTlsConversions.GetMinProtocol(EnabledProtocols),
                max = UnityTlsConversions.GetMaxProtocol(EnabledProtocols)
            };

            readCallback  = ReadCallback;
            writeCallback = WriteCallback;
            var callbacks = new UnityTls.unitytls_tlsctx_callbacks
            {
                write = writeCallback,
                read  = readCallback,
                data  = (void *)(IntPtr)handle
            };

            if (opts.ServerMode)
            {
                ExtractNativeKeyAndChainFromManagedCertificate(opts.ServerCertificate, &errorState, out var serverCerts,
                                                               out var serverPrivateKey);
                try
                {
                    var serverCertsRef = UnityTls.NativeInterface.unitytls_x509list_get_ref(serverCerts, &errorState);
                    var serverKeyRef   = UnityTls.NativeInterface.unitytls_key_get_ref(serverPrivateKey, &errorState);
                    Unity.Debug.CheckAndThrow(errorState, "Failed to parse server key/certificate");

                    tlsContext = UnityTls.NativeInterface.unitytls_tlsctx_create_server(protocolRange, callbacks,
                                                                                        serverCertsRef.handle, serverKeyRef.handle, &errorState);

                    if (opts.ClientCertificateRequired)
                    {
                        UnityTls.unitytls_x509list *clientAuthCAList = null;
                        try
                        {
                            clientAuthCAList = UnityTls.NativeInterface.unitytls_x509list_create(&errorState);
                            var clientAuthCAListRef =
                                UnityTls.NativeInterface.unitytls_x509list_get_ref(clientAuthCAList, &errorState);
                            UnityTls.NativeInterface.unitytls_tlsctx_server_require_client_authentication(tlsContext,
                                                                                                          clientAuthCAListRef, &errorState);
                        }
                        finally
                        {
                            UnityTls.NativeInterface.unitytls_x509list_free(clientAuthCAList);
                        }
                    }
                }
                finally
                {
                    UnityTls.NativeInterface.unitytls_x509list_free(serverCerts);
                    UnityTls.NativeInterface.unitytls_key_free(serverPrivateKey);
                }
            }
            else
            {
                var targetHostUtf8 = Encoding.UTF8.GetBytes(opts.TargetHost);
                fixed(byte *targetHostUtf8Ptr = targetHostUtf8)
                {
                    tlsContext = UnityTls.NativeInterface.unitytls_tlsctx_create_client(protocolRange, callbacks,
                                                                                        targetHostUtf8Ptr, (size_t)targetHostUtf8.Length, &errorState);
                }

                certificateCallback = CertificateCallback;
                UnityTls.NativeInterface.unitytls_tlsctx_set_certificate_callback(tlsContext, certificateCallback,
                                                                                  (void *)(IntPtr)handle, &errorState);
            }

            verifyCallback = VerifyCallback;
            UnityTls.NativeInterface.unitytls_tlsctx_set_x509verify_callback(tlsContext, verifyCallback,
                                                                             (void *)(IntPtr)handle, &errorState);

            Unity.Debug.CheckAndThrow(errorState, "Failed to create UnityTls context");

#pragma warning disable CS0162 // Disable unreachable code warning
            if (ActivateTracing)
            {
                traceCallback = TraceCallback;
                UnityTls.NativeInterface.unitytls_tlsctx_set_trace_callback(tlsContext, traceCallback, null,
                                                                            &errorState);
                Unity.Debug.CheckAndThrow(errorState, "Failed to set trace callback");
            }
#pragma warning restore CS0162 // Reenable unreachable code warning.

            hasContext = true;
        }