From b662a26c7e2b66d916986b1ed9ffae3b918619d1 Mon Sep 17 00:00:00 2001
From: Ac_K <Acoustik666@gmail.com>
Date: Tue, 13 Apr 2021 03:04:18 +0200
Subject: [PATCH] nifm/ssl: Implement GetCurrentNetworkProfile and stub Ssl
 Service (#2186)

* nifm/ssl: Implement GetCurrentNetworkProfile and stub Ssl Service

* remove InterfaceVersion
---
 .../Nifm/StaticService/IGeneralService.cs     |  47 ++++++--
 .../Nifm/StaticService/Types/DnsSetting.cs    |  15 ++-
 .../Nifm/StaticService/Types/IpSettingData.cs |  13 ++
 .../StaticService/Types/NetworkProfileData.cs |  17 +++
 .../Nifm/StaticService/Types/ProxySetting.cs  |  27 +++++
 .../Types/WirelessSettingData.cs              |  15 +++
 .../HOS/Services/Sockets/Nsd/IManager.cs      |   6 +-
 .../Sockets/Nsd/Manager/FqdnResolver.cs       |  34 ++----
 Ryujinx.HLE/HOS/Services/Ssl/ISslService.cs   |  16 ++-
 .../Services/Ssl/SslService/ISslConnection.cs | 113 ++++++++++++++++++
 .../Services/Ssl/SslService/ISslContext.cs    |  63 +++++++---
 .../Services/Ssl/Types/CertificateFormat.cs   |   8 ++
 Ryujinx.HLE/HOS/Services/Ssl/Types/IoMode.cs  |   8 ++
 .../HOS/Services/Ssl/Types/OptionType.cs      |  10 ++
 .../Services/Ssl/Types/SessionCacheMode.cs    |   9 ++
 .../HOS/Services/Ssl/Types/SslVersion.cs      |  15 +++
 .../HOS/Services/Ssl/Types/VerifyOption.cs    |  15 +++
 17 files changed, 375 insertions(+), 56 deletions(-)
 create mode 100644 Ryujinx.HLE/HOS/Services/Nifm/StaticService/Types/IpSettingData.cs
 create mode 100644 Ryujinx.HLE/HOS/Services/Nifm/StaticService/Types/NetworkProfileData.cs
 create mode 100644 Ryujinx.HLE/HOS/Services/Nifm/StaticService/Types/ProxySetting.cs
 create mode 100644 Ryujinx.HLE/HOS/Services/Nifm/StaticService/Types/WirelessSettingData.cs
 create mode 100644 Ryujinx.HLE/HOS/Services/Ssl/SslService/ISslConnection.cs
 create mode 100644 Ryujinx.HLE/HOS/Services/Ssl/Types/CertificateFormat.cs
 create mode 100644 Ryujinx.HLE/HOS/Services/Ssl/Types/IoMode.cs
 create mode 100644 Ryujinx.HLE/HOS/Services/Ssl/Types/OptionType.cs
 create mode 100644 Ryujinx.HLE/HOS/Services/Ssl/Types/SessionCacheMode.cs
 create mode 100644 Ryujinx.HLE/HOS/Services/Ssl/Types/SslVersion.cs
 create mode 100644 Ryujinx.HLE/HOS/Services/Ssl/Types/VerifyOption.cs

diff --git a/Ryujinx.HLE/HOS/Services/Nifm/StaticService/IGeneralService.cs b/Ryujinx.HLE/HOS/Services/Nifm/StaticService/IGeneralService.cs
index 944186d326..2614a54cab 100644
--- a/Ryujinx.HLE/HOS/Services/Nifm/StaticService/IGeneralService.cs
+++ b/Ryujinx.HLE/HOS/Services/Nifm/StaticService/IGeneralService.cs
@@ -2,8 +2,11 @@ using Ryujinx.Common;
 using Ryujinx.Common.Logging;
 using Ryujinx.HLE.HOS.Services.Nifm.StaticService.GeneralService;
 using Ryujinx.HLE.HOS.Services.Nifm.StaticService.Types;
+using Ryujinx.HLE.Utilities;
 using System;
 using System.Net.NetworkInformation;
+using System.Runtime.CompilerServices;
+using System.Text;
 
 namespace Ryujinx.HLE.HOS.Services.Nifm.StaticService
 {
@@ -51,6 +54,38 @@ namespace Ryujinx.HLE.HOS.Services.Nifm.StaticService
             return ResultCode.Success;
         }
 
+        [Command(5)]
+        // GetCurrentNetworkProfile() -> buffer<nn::nifm::detail::sf::NetworkProfileData, 0x1a, 0x17c>
+        public ResultCode GetCurrentNetworkProfile(ServiceCtx context)
+        {
+            long networkProfileDataPosition = context.Request.RecvListBuff[0].Position;
+
+            (IPInterfaceProperties interfaceProperties, UnicastIPAddressInformation unicastAddress) = GetLocalInterface();
+
+            if (interfaceProperties == null || unicastAddress == null)
+            {
+                return ResultCode.NoInternetConnection;
+            }
+
+            Logger.Info?.Print(LogClass.ServiceNifm, $"Console's local IP is \"{unicastAddress.Address}\".");
+
+            context.Response.PtrBuff[0] = context.Response.PtrBuff[0].WithSize(Unsafe.SizeOf<NetworkProfileData>());
+
+            NetworkProfileData networkProfile = new NetworkProfileData
+            {
+                Uuid = new UInt128(Guid.NewGuid().ToByteArray())
+            };
+
+            networkProfile.IpSettingData.IpAddressSetting = new IpAddressSetting(interfaceProperties, unicastAddress);
+            networkProfile.IpSettingData.DnsSetting       = new DnsSetting(interfaceProperties);
+
+            Encoding.ASCII.GetBytes("RyujinxNetwork").CopyTo(networkProfile.Name.ToSpan());
+
+            context.Memory.Write((ulong)networkProfileDataPosition, networkProfile);
+
+            return ResultCode.Success;
+        }
+
         [Command(12)]
         // GetCurrentIpAddress() -> nn::nifm::IpV4Address
         public ResultCode GetCurrentIpAddress(ServiceCtx context)
@@ -75,7 +110,7 @@ namespace Ryujinx.HLE.HOS.Services.Nifm.StaticService
         {
             (IPInterfaceProperties interfaceProperties, UnicastIPAddressInformation unicastAddress) = GetLocalInterface();
 
-            if (interfaceProperties == null)
+            if (interfaceProperties == null || unicastAddress == null)
             {
                 return ResultCode.NoInternetConnection;
             }
@@ -138,11 +173,11 @@ namespace Ryujinx.HLE.HOS.Services.Nifm.StaticService
             foreach (NetworkInterface adapter in interfaces)
             {
                 // Ignore loopback and non IPv4 capable interface.
-                if (adapter.NetworkInterfaceType != NetworkInterfaceType.Loopback && adapter.Supports(NetworkInterfaceComponent.IPv4))
+                if (targetProperties == null && adapter.NetworkInterfaceType != NetworkInterfaceType.Loopback && adapter.Supports(NetworkInterfaceComponent.IPv4))
                 {
                     IPInterfaceProperties properties = adapter.GetIPProperties();
 
-                    if (properties.GatewayAddresses.Count > 0 && properties.DnsAddresses.Count > 1)
+                    if (properties.GatewayAddresses.Count > 0 && properties.DnsAddresses.Count > 0)
                     {
                         foreach (UnicastIPAddressInformation info in properties.UnicastAddresses)
                         {
@@ -156,12 +191,6 @@ namespace Ryujinx.HLE.HOS.Services.Nifm.StaticService
                             }
                         }
                     }
-
-                    // Found the target interface, stop here.
-                    if (targetProperties != null)
-                    {
-                        break;
-                    }
                 }
             }
 
diff --git a/Ryujinx.HLE/HOS/Services/Nifm/StaticService/Types/DnsSetting.cs b/Ryujinx.HLE/HOS/Services/Nifm/StaticService/Types/DnsSetting.cs
index 96f99dfa75..d6381fba67 100644
--- a/Ryujinx.HLE/HOS/Services/Nifm/StaticService/Types/DnsSetting.cs
+++ b/Ryujinx.HLE/HOS/Services/Nifm/StaticService/Types/DnsSetting.cs
@@ -14,8 +14,17 @@ namespace Ryujinx.HLE.HOS.Services.Nifm.StaticService.Types
         public DnsSetting(IPInterfaceProperties interfaceProperties)
         {
             IsDynamicDnsEnabled = interfaceProperties.IsDynamicDnsEnabled;
-            PrimaryDns          = new IpV4Address(interfaceProperties.DnsAddresses[0]);
-            SecondaryDns        = new IpV4Address(interfaceProperties.DnsAddresses[1]);
+
+            if (interfaceProperties.DnsAddresses.Count == 0)
+            {
+                PrimaryDns   = new IpV4Address();
+                SecondaryDns = new IpV4Address();
+            }
+            else
+            {
+                PrimaryDns   = new IpV4Address(interfaceProperties.DnsAddresses[0]);
+                SecondaryDns = new IpV4Address(interfaceProperties.DnsAddresses[interfaceProperties.DnsAddresses.Count > 1 ? 1 : 0]);
+            }
         }
     }
-}
+}
\ No newline at end of file
diff --git a/Ryujinx.HLE/HOS/Services/Nifm/StaticService/Types/IpSettingData.cs b/Ryujinx.HLE/HOS/Services/Nifm/StaticService/Types/IpSettingData.cs
new file mode 100644
index 0000000000..8ffe824c8b
--- /dev/null
+++ b/Ryujinx.HLE/HOS/Services/Nifm/StaticService/Types/IpSettingData.cs
@@ -0,0 +1,13 @@
+using System.Runtime.InteropServices;
+
+namespace Ryujinx.HLE.HOS.Services.Nifm.StaticService.Types
+{
+    [StructLayout(LayoutKind.Sequential, Pack = 1, Size = 0xc2)]
+    struct IpSettingData
+    {
+        public IpAddressSetting IpAddressSetting;
+        public DnsSetting       DnsSetting;
+        public ProxySetting     ProxySetting;
+        public short            Mtu;
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx.HLE/HOS/Services/Nifm/StaticService/Types/NetworkProfileData.cs b/Ryujinx.HLE/HOS/Services/Nifm/StaticService/Types/NetworkProfileData.cs
new file mode 100644
index 0000000000..3c86aed503
--- /dev/null
+++ b/Ryujinx.HLE/HOS/Services/Nifm/StaticService/Types/NetworkProfileData.cs
@@ -0,0 +1,17 @@
+using Ryujinx.Common.Memory;
+using Ryujinx.HLE.Utilities;
+using System.Runtime.InteropServices;
+
+namespace Ryujinx.HLE.HOS.Services.Nifm.StaticService.Types
+{
+    [StructLayout(LayoutKind.Sequential, Pack = 1, Size = 0x17C)]
+    struct NetworkProfileData
+    {
+        public IpSettingData       IpSettingData;
+        public UInt128             Uuid;
+        public Array64<byte>       Name;
+        public Array4<byte>        Unknown;
+        public WirelessSettingData WirelessSettingData;
+        public byte                Padding;
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx.HLE/HOS/Services/Nifm/StaticService/Types/ProxySetting.cs b/Ryujinx.HLE/HOS/Services/Nifm/StaticService/Types/ProxySetting.cs
new file mode 100644
index 0000000000..827520f15d
--- /dev/null
+++ b/Ryujinx.HLE/HOS/Services/Nifm/StaticService/Types/ProxySetting.cs
@@ -0,0 +1,27 @@
+using LibHac.Common;
+using Ryujinx.Common.Memory;
+using System;
+using System.Runtime.InteropServices;
+
+namespace Ryujinx.HLE.HOS.Services.Nifm.StaticService.Types
+{
+    [StructLayout(LayoutKind.Sequential, Pack = 1, Size = 0xaa)]
+    public struct ProxySetting
+    {
+        [MarshalAs(UnmanagedType.I1)]
+        public  bool          Enabled;
+        private byte          _padding;
+        public  short         Port;
+        private NameStruct    _name;
+        [MarshalAs(UnmanagedType.I1)]
+        public  bool          AutoAuthEnabled;
+        public  Array32<byte> User;
+        public  Array32<byte> Pass;
+        private byte          _padding2;
+
+        [StructLayout(LayoutKind.Sequential, Size = 0x64)]
+        private struct NameStruct { }
+
+        public Span<byte> Name => SpanHelpers.AsSpan<NameStruct, byte>(ref _name);
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx.HLE/HOS/Services/Nifm/StaticService/Types/WirelessSettingData.cs b/Ryujinx.HLE/HOS/Services/Nifm/StaticService/Types/WirelessSettingData.cs
new file mode 100644
index 0000000000..8aa122c7df
--- /dev/null
+++ b/Ryujinx.HLE/HOS/Services/Nifm/StaticService/Types/WirelessSettingData.cs
@@ -0,0 +1,15 @@
+using Ryujinx.Common.Memory;
+using System.Runtime.InteropServices;
+
+namespace Ryujinx.HLE.HOS.Services.Nifm.StaticService.Types
+{
+    [StructLayout(LayoutKind.Sequential, Pack = 1, Size = 0x65)]
+    struct WirelessSettingData
+    {
+        public byte          SsidLength;
+        public Array32<byte> Ssid;
+        public Array3<byte>  Unknown;
+        public Array64<byte> Passphrase1;
+        public byte          Passphrase2;
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx.HLE/HOS/Services/Sockets/Nsd/IManager.cs b/Ryujinx.HLE/HOS/Services/Sockets/Nsd/IManager.cs
index 90f22dfb43..3dc6b245a5 100644
--- a/Ryujinx.HLE/HOS/Services/Sockets/Nsd/IManager.cs
+++ b/Ryujinx.HLE/HOS/Services/Sockets/Nsd/IManager.cs
@@ -132,7 +132,8 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Nsd
         // Resolve(buffer<unknown<0x100>, 0x15>) -> buffer<unknown<0x100>, 0x16>
         public ResultCode Resolve(ServiceCtx context)
         {
-            (long outputPosition, long outputSize) = context.Request.GetBufferType0x22();
+            long outputPosition = context.Request.ReceiveBuff[0].Position;
+            long outputSize     = context.Request.ReceiveBuff[0].Size;
 
             ResultCode result = _fqdnResolver.ResolveEx(context, out ResultCode errorCode, out string resolvedAddress);
 
@@ -147,7 +148,8 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Nsd
         // ResolveEx(buffer<unknown<0x100>, 0x15>) -> (u32, buffer<unknown<0x100>, 0x16>)
         public ResultCode ResolveEx(ServiceCtx context)
         {
-            (long outputPosition, long outputSize) = context.Request.GetBufferType0x22();
+            long outputPosition = context.Request.ReceiveBuff[0].Position;
+            long outputSize     = context.Request.ReceiveBuff[0].Size;
 
             ResultCode result = _fqdnResolver.ResolveEx(context, out ResultCode errorCode, out string resolvedAddress);
 
diff --git a/Ryujinx.HLE/HOS/Services/Sockets/Nsd/Manager/FqdnResolver.cs b/Ryujinx.HLE/HOS/Services/Sockets/Nsd/Manager/FqdnResolver.cs
index 3bdf15a1af..0c5dac1723 100644
--- a/Ryujinx.HLE/HOS/Services/Sockets/Nsd/Manager/FqdnResolver.cs
+++ b/Ryujinx.HLE/HOS/Services/Sockets/Nsd/Manager/FqdnResolver.cs
@@ -73,30 +73,19 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Nsd.Manager
                     return ResultCode.SettingsNotLoaded;
                 }
 
-                switch (address)
+                resolvedAddress = address switch
                 {
-                    case "e97b8a9d672e4ce4845ec6947cd66ef6-sb-api.accounts.nintendo.com": // dp1 environment
-                        resolvedAddress = "e97b8a9d672e4ce4845ec6947cd66ef6-sb.baas.nintendo.com";
-                        break;
-                    case "api.accounts.nintendo.com": // dp1 environment
-                        resolvedAddress = "e0d67c509fb203858ebcb2fe3f88c2aa.baas.nintendo.com";
-                        break;
-                    case "e97b8a9d672e4ce4845ec6947cd66ef6-sb.accounts.nintendo.com": // lp1 environment
-                        resolvedAddress = "e97b8a9d672e4ce4845ec6947cd66ef6-sb.baas.nintendo.com";
-                        break;
-                    case "accounts.nintendo.com": // lp1 environment
-                        resolvedAddress = "e0d67c509fb203858ebcb2fe3f88c2aa.baas.nintendo.com";
-                        break;
+                    "e97b8a9d672e4ce4845ec6947cd66ef6-sb-api.accounts.nintendo.com" => "e97b8a9d672e4ce4845ec6947cd66ef6-sb.baas.nintendo.com", // dp1 environment
+                    "api.accounts.nintendo.com"                                     => "e0d67c509fb203858ebcb2fe3f88c2aa.baas.nintendo.com",    // dp1 environment
+                    "e97b8a9d672e4ce4845ec6947cd66ef6-sb.accounts.nintendo.com"     => "e97b8a9d672e4ce4845ec6947cd66ef6-sb.baas.nintendo.com", // lp1 environment
+                    "accounts.nintendo.com"                                         => "e0d67c509fb203858ebcb2fe3f88c2aa.baas.nintendo.com",    // lp1 environment
                     /*
-                    // TODO: Determine fields of the struct.
-                    case "": // + 0xEB8 || + 0x2BE8
-                        resolvedAddress = ""; // + 0xEB8 + 0x300 || + 0x2BE8 + 0x300
-                        break;
+                        // TODO: Determine fields of the struct.
+                        this + 0xEB8  => this + 0xEB8 + 0x300
+                        this + 0x2BE8 => this + 0x2BE8 + 0x300
                     */
-                    default:
-                        resolvedAddress = address;
-                        break;
-                }
+                    _ => address,
+                };
             }
             else
             {
@@ -108,7 +97,8 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Nsd.Manager
 
         public ResultCode ResolveEx(ServiceCtx context, out ResultCode resultCode, out string resolvedAddress)
         {
-            (long inputPosition, long inputSize)  = context.Request.GetBufferType0x21();
+            long inputPosition = context.Request.SendBuff[0].Position;
+            long inputSize     = context.Request.SendBuff[0].Size;
 
             byte[] addressBuffer = new byte[inputSize];
 
diff --git a/Ryujinx.HLE/HOS/Services/Ssl/ISslService.cs b/Ryujinx.HLE/HOS/Services/Ssl/ISslService.cs
index 7cc153200c..b99e7b56fa 100644
--- a/Ryujinx.HLE/HOS/Services/Ssl/ISslService.cs
+++ b/Ryujinx.HLE/HOS/Services/Ssl/ISslService.cs
@@ -1,24 +1,27 @@
 using Ryujinx.Common.Logging;
 using Ryujinx.HLE.HOS.Services.Ssl.SslService;
+using Ryujinx.HLE.HOS.Services.Ssl.Types;
 
 namespace Ryujinx.HLE.HOS.Services.Ssl
 {
     [Service("ssl")]
     class ISslService : IpcService
     {
+        // NOTE: The SSL service is used by games to connect it to various official online services, which we do not intend to support.
+        //       In this case it is acceptable to stub all calls of the service.
         public ISslService(ServiceCtx context) { }
 
         [Command(0)]
         // CreateContext(nn::ssl::sf::SslVersion, u64, pid) -> object<nn::ssl::sf::ISslContext>
         public ResultCode CreateContext(ServiceCtx context)
         {
-            int  sslVersion = context.RequestData.ReadInt32();
-            long unknown    = context.RequestData.ReadInt64();
-
-            Logger.Stub?.PrintStub(LogClass.ServiceSsl, new { sslVersion, unknown });
+            SslVersion sslVersion     = (SslVersion)context.RequestData.ReadUInt32();
+            ulong      pidPlaceholder = context.RequestData.ReadUInt64();
 
             MakeObject(context, new ISslContext(context));
 
+            Logger.Stub?.PrintStub(LogClass.ServiceSsl, new { sslVersion });
+
             return ResultCode.Success;
         }
 
@@ -26,9 +29,10 @@ namespace Ryujinx.HLE.HOS.Services.Ssl
         // SetInterfaceVersion(u32)
         public ResultCode SetInterfaceVersion(ServiceCtx context)
         {
-            int version = context.RequestData.ReadInt32();
+            // 1 = 3.0.0+, 2 = 5.0.0+, 3 = 6.0.0+
+            uint interfaceVersion = context.RequestData.ReadUInt32();
 
-            Logger.Stub?.PrintStub(LogClass.ServiceSsl, new { version });
+            Logger.Stub?.PrintStub(LogClass.ServiceSsl, new { interfaceVersion });
 
             return ResultCode.Success;
         }
diff --git a/Ryujinx.HLE/HOS/Services/Ssl/SslService/ISslConnection.cs b/Ryujinx.HLE/HOS/Services/Ssl/SslService/ISslConnection.cs
new file mode 100644
index 0000000000..d039154c53
--- /dev/null
+++ b/Ryujinx.HLE/HOS/Services/Ssl/SslService/ISslConnection.cs
@@ -0,0 +1,113 @@
+using Ryujinx.Common.Logging;
+using Ryujinx.HLE.HOS.Services.Ssl.Types;
+using System.Text;
+
+namespace Ryujinx.HLE.HOS.Services.Ssl.SslService
+{
+    class ISslConnection : IpcService
+    {
+        public ISslConnection() { }
+
+        [Command(0)]
+        // SetSocketDescriptor(u32) -> u32
+        public ResultCode SetSocketDescriptor(ServiceCtx context)
+        {
+            uint socketFd          = context.RequestData.ReadUInt32();
+            uint duplicateSocketFd = 0;
+
+            context.ResponseData.Write(duplicateSocketFd);
+
+            Logger.Stub?.PrintStub(LogClass.ServiceSsl, new { socketFd });
+
+            return ResultCode.Success;
+        }
+
+        [Command(1)]
+        // SetHostName(buffer<bytes, 5>)
+        public ResultCode SetHostName(ServiceCtx context)
+        {
+            long hostNameDataPosition = context.Request.SendBuff[0].Position;
+            long hostNameDataSize     = context.Request.SendBuff[0].Size;
+
+            byte[] hostNameData = new byte[hostNameDataSize];
+
+            context.Memory.Read((ulong)hostNameDataPosition, hostNameData);
+
+            string hostName = Encoding.ASCII.GetString(hostNameData).Trim('\0');
+
+            Logger.Stub?.PrintStub(LogClass.ServiceSsl, new { hostName });
+
+            return ResultCode.Success;
+        }
+
+        [Command(2)]
+        // SetVerifyOption(nn::ssl::sf::VerifyOption)
+        public ResultCode SetVerifyOption(ServiceCtx context)
+        {
+            VerifyOption verifyOption = (VerifyOption)context.RequestData.ReadUInt32();
+
+            Logger.Stub?.PrintStub(LogClass.ServiceSsl, new { verifyOption });
+
+            return ResultCode.Success;
+        }
+
+        [Command(3)]
+        // SetIoMode(nn::ssl::sf::IoMode)
+        public ResultCode SetIoMode(ServiceCtx context)
+        {
+            IoMode ioMode = (IoMode)context.RequestData.ReadUInt32();
+
+            Logger.Stub?.PrintStub(LogClass.ServiceSsl, new { ioMode });
+
+            return ResultCode.Success;
+        }
+
+        [Command(8)]
+        // DoHandshake()
+        public ResultCode DoHandshake(ServiceCtx context)
+        {
+            Logger.Stub?.PrintStub(LogClass.ServiceSsl);
+
+            return ResultCode.Success;
+        }
+
+        [Command(11)]
+        // Write(buffer<bytes, 5>) -> u32
+        public ResultCode Write(ServiceCtx context)
+        {
+            long inputDataPosition = context.Request.SendBuff[0].Position;
+            long inputDataSize     = context.Request.SendBuff[0].Size;
+
+            uint transferredSize = 0;
+
+            context.ResponseData.Write(transferredSize);
+
+            Logger.Stub?.PrintStub(LogClass.ServiceSsl);
+
+            return ResultCode.Success;
+        }
+
+        [Command(17)]
+        // SetSessionCacheMode(nn::ssl::sf::SessionCacheMode)
+        public ResultCode SetSessionCacheMode(ServiceCtx context)
+        {
+            SessionCacheMode sessionCacheMode = (SessionCacheMode)context.RequestData.ReadUInt32();
+
+            Logger.Stub?.PrintStub(LogClass.ServiceSsl, new { sessionCacheMode });
+
+            return ResultCode.Success;
+        }
+
+        [Command(22)]
+        // SetOption(b8, nn::ssl::sf::OptionType)
+        public ResultCode SetOption(ServiceCtx context)
+        {
+            bool       optionEnabled = context.RequestData.ReadBoolean();
+            OptionType optionType    = (OptionType)context.RequestData.ReadUInt32();
+
+            Logger.Stub?.PrintStub(LogClass.ServiceSsl, new { optionType, optionEnabled });
+
+            return ResultCode.Success;
+        }
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx.HLE/HOS/Services/Ssl/SslService/ISslContext.cs b/Ryujinx.HLE/HOS/Services/Ssl/SslService/ISslContext.cs
index b9ca92b045..5341f3603c 100644
--- a/Ryujinx.HLE/HOS/Services/Ssl/SslService/ISslContext.cs
+++ b/Ryujinx.HLE/HOS/Services/Ssl/SslService/ISslContext.cs
@@ -1,27 +1,62 @@
 using Ryujinx.Common.Logging;
-using System;
+using Ryujinx.HLE.HOS.Services.Ssl.Types;
+using System.Text;
 
 namespace Ryujinx.HLE.HOS.Services.Ssl.SslService
 {
     class ISslContext : IpcService
     {
+        private ulong _serverCertificateId;
+        private ulong _clientCertificateId;
+
         public ISslContext(ServiceCtx context) { }
-        
-        [Command(4)]
-        // ImportServerPki(nn::ssl::sf::CertificateFormat certificateFormat, buffer<bytes, 5> certificate) -> u64 certificateId
-        public ResultCode ImportServerPki(ServiceCtx context)
+
+        [Command(2)]
+        // CreateConnection() -> object<nn::ssl::sf::ISslConnection>
+        public ResultCode CreateConnection(ServiceCtx context)
         {
-            int   certificateFormat       = context.RequestData.ReadInt32();
-            long  certificateDataPosition = context.Request.SendBuff[0].Position;
-            long  certificateDataSize     = context.Request.SendBuff[0].Size;
-            ulong certificateId           = 1;
-
-            context.ResponseData.Write(certificateId);
-
-            Logger.Stub?.PrintStub(LogClass.ServiceSsl, new { certificateFormat, certificateDataPosition, certificateDataSize });
+            MakeObject(context, new ISslConnection());
 
             return ResultCode.Success;
         }
 
+        [Command(4)]
+        // ImportServerPki(nn::ssl::sf::CertificateFormat certificateFormat, buffer<bytes, 5> certificate) -> u64 certificateId
+        public ResultCode ImportServerPki(ServiceCtx context)
+        {
+            CertificateFormat certificateFormat = (CertificateFormat)context.RequestData.ReadUInt32();
+
+            long certificateDataPosition = context.Request.SendBuff[0].Position;
+            long certificateDataSize     = context.Request.SendBuff[0].Size;
+
+            context.ResponseData.Write(_serverCertificateId++);
+
+            Logger.Stub?.PrintStub(LogClass.ServiceSsl, new { certificateFormat });
+
+            return ResultCode.Success;
+        }
+
+        [Command(5)]
+        // ImportClientPki(buffer<bytes, 5> certificate, buffer<bytes, 5> ascii_password) -> u64 certificateId
+        public ResultCode ImportClientPki(ServiceCtx context)
+        {
+            long certificateDataPosition = context.Request.SendBuff[0].Position;
+            long certificateDataSize     = context.Request.SendBuff[0].Size;
+
+            long asciiPasswordDataPosition = context.Request.SendBuff[1].Position;
+            long asciiPasswordDataSize     = context.Request.SendBuff[1].Size;
+
+            byte[] asciiPasswordData = new byte[asciiPasswordDataSize];
+
+            context.Memory.Read((ulong)asciiPasswordDataPosition, asciiPasswordData);
+
+            string asciiPassword = Encoding.ASCII.GetString(asciiPasswordData).Trim('\0');
+
+            context.ResponseData.Write(_clientCertificateId++);
+
+            Logger.Stub?.PrintStub(LogClass.ServiceSsl, new { asciiPassword });
+
+            return ResultCode.Success;
+        }
     }
-}
+}
\ No newline at end of file
diff --git a/Ryujinx.HLE/HOS/Services/Ssl/Types/CertificateFormat.cs b/Ryujinx.HLE/HOS/Services/Ssl/Types/CertificateFormat.cs
new file mode 100644
index 0000000000..1d80f739e3
--- /dev/null
+++ b/Ryujinx.HLE/HOS/Services/Ssl/Types/CertificateFormat.cs
@@ -0,0 +1,8 @@
+namespace Ryujinx.HLE.HOS.Services.Ssl.Types
+{
+    enum CertificateFormat : uint
+    {
+        Pem = 1,
+        Der = 2
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx.HLE/HOS/Services/Ssl/Types/IoMode.cs b/Ryujinx.HLE/HOS/Services/Ssl/Types/IoMode.cs
new file mode 100644
index 0000000000..1cd06d6de2
--- /dev/null
+++ b/Ryujinx.HLE/HOS/Services/Ssl/Types/IoMode.cs
@@ -0,0 +1,8 @@
+namespace Ryujinx.HLE.HOS.Services.Ssl.Types
+{
+    enum IoMode : uint
+    {
+        Blocking    = 1,
+        NonBlocking = 2
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx.HLE/HOS/Services/Ssl/Types/OptionType.cs b/Ryujinx.HLE/HOS/Services/Ssl/Types/OptionType.cs
new file mode 100644
index 0000000000..3673200a4a
--- /dev/null
+++ b/Ryujinx.HLE/HOS/Services/Ssl/Types/OptionType.cs
@@ -0,0 +1,10 @@
+namespace Ryujinx.HLE.HOS.Services.Ssl.Types
+{
+    enum OptionType : uint
+    {
+        DoNotCloseSocket,
+        GetServerCertChain, // 3.0.0+
+        SkipDefaultVerify,  // 5.0.0+
+        EnableAlpn          // 9.0.0+
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx.HLE/HOS/Services/Ssl/Types/SessionCacheMode.cs b/Ryujinx.HLE/HOS/Services/Ssl/Types/SessionCacheMode.cs
new file mode 100644
index 0000000000..cec7b745e4
--- /dev/null
+++ b/Ryujinx.HLE/HOS/Services/Ssl/Types/SessionCacheMode.cs
@@ -0,0 +1,9 @@
+namespace Ryujinx.HLE.HOS.Services.Ssl.Types
+{
+    enum SessionCacheMode : uint
+    {
+        None,
+        SessionId,
+        SessionTicket
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx.HLE/HOS/Services/Ssl/Types/SslVersion.cs b/Ryujinx.HLE/HOS/Services/Ssl/Types/SslVersion.cs
new file mode 100644
index 0000000000..a8897802ae
--- /dev/null
+++ b/Ryujinx.HLE/HOS/Services/Ssl/Types/SslVersion.cs
@@ -0,0 +1,15 @@
+using System;
+
+namespace Ryujinx.HLE.HOS.Services.Ssl.Types
+{
+    [Flags]
+    enum SslVersion : uint
+    {
+        Auto   = 1 << 0,
+        TlsV10 = 1 << 3,
+        TlsV11 = 1 << 4,
+        TlsV12 = 1 << 5,
+        TlsV13 = 1 << 6, // 11.0.0+
+        Auto2  = 1 << 24 // 11.0.0+
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx.HLE/HOS/Services/Ssl/Types/VerifyOption.cs b/Ryujinx.HLE/HOS/Services/Ssl/Types/VerifyOption.cs
new file mode 100644
index 0000000000..d25bb6c343
--- /dev/null
+++ b/Ryujinx.HLE/HOS/Services/Ssl/Types/VerifyOption.cs
@@ -0,0 +1,15 @@
+using System;
+
+namespace Ryujinx.HLE.HOS.Services.Ssl.Types
+{
+    [Flags]
+    enum VerifyOption : uint
+    {
+        PeerCa            = 1 << 0,
+        HostName          = 1 << 1,
+        DateCheck         = 1 << 2,
+        EvCertPartial     = 1 << 3,
+        EvPolicyOid       = 1 << 4, // 6.0.0+
+        EvCertFingerprint = 1 << 5  // 6.0.0+
+    }
+}
\ No newline at end of file