SSL services

From Nintendo Switch Brew
Jump to navigation Jump to search

ssl

This is "nn::ssl::sf::ISslService". sdknso uses SessionManager with this, where the additional session-count is user-specified (default is 0x2). An error is thrown when the input value is less than 1 or >4.

This service implements client-mode TLS using NSS.

Note that SystemPrograms generally use #RegisterInternalPki (see libcurl). However, web-applets use #GetCertificates with #CaCertificateId All - #RegisterInternalPki is not used.

Cmd Name
0 #CreateContext
1 #GetContextCount
2 #GetCertificates
3 #GetCertificateBufSize
4 [3.0.0+] #DebugIoctl
5 [3.0.0+] #SetInterfaceVersion
6 [5.0.0+] #FlushSessionCache
7 [6.0.0+] #SetDebugOption
8 [6.0.0+] #GetDebugOption

CreateContext

Takes a PID, an input u32 #SslVersion, an input u64 pid_placeholder, and returns an output #ISslContext.

GetContextCount

No input, returns an output u32.

This is not exposed by sdknso.

GetCertificates

Takes a type-0x6 output buffer and a type-0x5 input buffer containing an array of #CaCertificateId.

[3.0.0+] This now returns an output u32 for actual total output entries.

The output buffer starts with an array of #BuiltInCertificateInfo, with the DER cert data following afterwards.

GetCertificateBufSize

Takes a type-0x5 input buffer containing an array of #CaCertificateId, returns an output u32 for the size to use with #GetCertificates.

DebugIoctl

Stubbed on retail, just returns an error.

SetInterfaceVersion

Takes an input u32 version, no output.

Used by user-processes during service init.

Value SystemVersion
0x1 [3.0.0+]
0x2 [5.0.0+]
0x3 [6.0.0+]

FlushSessionCache

Takes a type-0x5 input buffer, an input u32 #FlushSessionCacheOptionType, returns an output u32.

The input buffer contains a NUL-terminated string, which is only used when the type is value 0. For type 1, an empty buffer is passed (addr=NULL/size=0).

SetDebugOption

Takes an input u32 #DebugOptionType and a type-0x5 input buffer, no output.

The input u32 value must be 0, and the buffer addr/size must not be 0.

The u8 at buf+0 is copied to state.

The nn::ssl::SetDebugOption func in sdknso just verifies the input and that the service is initialized, without actually using the cmd.

GetDebugOption

Takes an input u32 #DebugOptionType and a type-0x6 output buffer.

Same as #SetDebugOption except this copies state to the buffer instead.

ISslContext

This is "nn::ssl::sf::ISslContext".

Cmd Name
0 #SetOption
1 #GetOption
2 #CreateConnection
3 #GetConnectionCount
4 #ImportServerPki
5 #ImportClientPki
6 #RemoveServerPki
7 #RemoveClientPki
8 #RegisterInternalPki
9 #AddPolicyOid
10 [3.0.0+] #ImportCrl
11 [3.0.0+] #RemoveCrl

SetOption

Takes an input #ContextOption and an input s32, no output.

With #ContextOption value 1, the s32 has to be 0 or 1 (state field is set to the s32 value).

Prior to 4.x this is stubbed.

GetOption

Takes an input #ContextOption, returns an output s32.

Prior to 4.x this is stubbed.

CreateConnection

No input, returns an #ISslConnection.

GetConnectionCount

No input, returns an output u32.

This is not exposed by sdknso. Immediately prior to closing the #ISslContext object, sdknso uses this cmd, returning the error from there on failure. An error is also thrown if the output count is non-zero.

ImportServerPki

Takes a type-0x5 input buffer and a #CertificateFormat, returns an output u64 Id.

The input buffer can contain multiple certs. A maximum of 71 ServerPki objects (associated with the output Id) can be imported.

The certs can be CAs or server certs (no pubkeys).

ImportClientPki

Takes two type-0x5 input buffers, returns an output u64 Id.

The first buffer contains the PKCS#12 data, the second buffer contains the optional (addr=NULL/size=0) ASCII password. The password is copied to a heap buffer with size+1, for NUL-termination.

An error is thrown if this cmd or #RegisterInternalPki was already used previously.

RemoveServerPki

Takes an input u64 Id, no output.

RemoveClientPki

Takes an input u64 Id, no output.

RegisterInternalPki

Takes an input #InternalPki, returns an output u64 Id.

An error is thrown if this cmd or #ImportClientPki was already used previously.

AddPolicyOid

Takes a type-0x5 input buffer, no output.

The buffer contains a string. The string length must not match the buffer size, and the string length must be <=0xFE.

ImportCrl

Takes a type-0x5 input buffer, returns an output u64 Id.

The input buffer contains the DER CRL.

RemoveCrl

Takes an input u64 Id, no output.

ISslConnection

This is "nn::ssl::sf::ISslConnection".

Cmd Name
0 #SetSocketDescriptor
1 #SetHostName
2 #SetVerifyOption
3 #SetIoMode
4 #GetSocketDescriptor
5 #GetHostName
6 #GetVerifyOption
7 #GetIoMode
8 #DoHandshake
9 #DoHandshakeGetServerCert
10 #Read
11 #Write
12 #Pending
13 #Peek
14 #Poll
15 #GetVerifyCertError
16 #GetNeededServerCertBufferSize
17 #SetSessionCacheMode
18 #GetSessionCacheMode
19 #FlushSessionCache
20 #SetRenegotiationMode
21 #GetRenegotiationMode
22 SetOption
23 GetOption
24 #GetVerifyCertErrors
25 [4.0.0+] #GetCipherInfo
26 [9.0.0+] #SetNextAlpnProto
27 [9.0.0+] #GetNextAlpnProto

SetSocketDescriptor

Takes an input s32 sockfd, returns an output s32 sockfd.

An error is thrown if this was used previously.

internal_sockfd = output from DuplicateSocket, with the input sockfd. If the field which would be used for the input u64 (PID set by #CreateContext) is zero however, it instead uses internal_sockfd=input_sockfd directly without DuplicateSocket and later returns -1 for the sockfd. An error is thrown if DuplicateSocket fails. The input sockfd is later returned as the output sockfd, however if DoNotCloseSocket is set it will instead return -1 for the sockfd. Then GetPeerName is used with internal_sockfd, throwing an error if this fails. internal_sockfd is used for state initialization, and the input_sockfd is written into state.

Immediately prior to closing the #ISslConnection object, sdknso will close the sockfd which was returned by this cmd if it is not negative.

SetHostName

Takes a type-0x5 input buffer, no output.

The input buffer contains a string, the buffer size must be <=0xFF.

The buffer is copied to a tmpbuf, then nsd ResolveEx is used with this tmpbuf to set the HostName in state.

SetVerifyOption

Takes an input u32 #VerifyOption, no output.

SetIoMode

Takes an input #IoMode, no output.

#SetSocketDescriptor must have been used prior to this successfully.

GetSocketDescriptor

No input, returns an output s32.

#SetSocketDescriptor must have been used prior to this successfully.

This gets the input_sockfd which was previously saved in state by #SetSocketDescriptor.

GetHostName

Takes a type-0x6 output buffer, returns an output u32.

The output u32 is the string length (buffer must be large enough for the entire string).

GetVerifyOption

No input, returns an output u32 #VerifyOption.

GetIoMode

No input, returns an output #IoMode.

DoHandshake

No input/output.

#SetSocketDescriptor must have been used prior to this successfully.

The hostname must be set (non-empty string) when #VerifyOption HostName is set, otherwise an error is thrown.

This will also set a callback eventually for saving an error-report when needed, which just contains the ErrorCode loaded from a Result in state. This sysmodule doesn't save any other reports elsewhere.

DoHandshakeGetServerCert

Takes a type-0x6 output buffer, returns two output u32s.

Same as #DoHandshake except the params for the func called internally are user-specified, instead of all 0.

The buffer contains the output server cert DER. The first u32 is the output size, the second u32 is the total certs in the buffer.

When GetServerCertChain is set, the output buffer contains the full chain. This buffer can then be parsed by a seperate sdknso func:

  • The header is at +0. +0 must match a magicnum (0x4E4D684374726543 "CertChMN"), and the u32 at +0x4 must be larger than the input cert_index.
  • The data at +0x10 is the 0x8-byte array-entries, for each cert. Entry +0x0 is the u32 size, and +0x4 is the u32 offset. These are copied to the output "nn::ssl::Connection::ServerCertDetail" struct, for the entry with the input cert_index: +0 = u32 size, +8 = address (input_buffer+offset).

No certs are returned when PeerCa is not set.

When #IoMode is NonBlocking the buffer will be only filled in once - when this cmd returns successfully the buffer will generally be empty.

Read

Takes a type-0x6 output buffer, returns an output u32.

#SetSocketDescriptor must have been used prior to this successfully.

The output u32 is the actual transferred size.

Write

Takes a type-0x5 input buffer, returns an output u32.

#SetSocketDescriptor must have been used prior to this successfully.

The output u32 is the actual transferred size.

Pending

No input, returns an output s32.

#SetSocketDescriptor must have been used prior to this successfully.

Peek

Takes a type-0x6 output buffer, returns an output u32 size.

#SetSocketDescriptor must have been used prior to this successfully.

Poll

Takes an input #PollEvent, an u32, returns an output #PollEvent.

#SetSocketDescriptor must have been used prior to this successfully.

The u32 is the timeout in milliseconds.

GetVerifyCertError

No input/output.

This loads a field from state, clears the original value in state, and returns the Result from calling a conversion func with the loaded value.

GetNeededServerCertBufferSize

No input, returns an output u32.

This just copies an u32 from state to output and returns 0.

SetSessionCacheMode

Takes an input #SessionCacheMode, no output.

#SetSocketDescriptor must have been used prior to this successfully.

GetSessionCacheMode

No input, returns an output #SessionCacheMode.

#SetSocketDescriptor must have been used prior to this successfully.

FlushSessionCache

No input/output.

#SetSocketDescriptor must have been used prior to this successfully.

SetRenegotiationMode

Takes an input #RenegotiationMode, no output.

#SetSocketDescriptor must have been used prior to this successfully.

GetRenegotiationMode

No input, returns an output #RenegotiationMode.

#SetSocketDescriptor must have been used prior to this successfully.

SetOption

Takes an input u8 bool and an #OptionType, no output.

GetOption

Takes an input #OptionType, returns an output u8 bool.

GetVerifyCertErrors

Takes a type-0x6 output buffer, returns two output u32s.

On success, sdknso will throw an error if the two output u32s don't match.

GetCipherInfo

Takes an input u32 and a type-0x6 output buffer.

sdknso uses hard-coded value 0x1 for the u32. The output buffer contains #CipherInfo.

Errors are thrown if the input u32 doesn't match 0x1, or if the buffer size doesn't match the size for #CipherInfo.

#SetSocketDescriptor must have been used prior to this successfully.

SetNextAlpnProto

Takes a type-0x5 input buffer, no output.

#SetSocketDescriptor must have been used prior to this successfully.

The buffer size must be at least 0x2.

EnableAlpn should be set at the time of using DoHandshake*, otherwise using this cmd will have no affect.

The buffer contains an array of {u8 size, {data with the specified size}}, which must be within the buffer-size bounds.

GetNextAlpnProto

Takes a type-0x6 output buffer, returns an output #AlpnProtoState and an output u32.

#SetSocketDescriptor must have been used prior to this successfully.

The buffer contains the output string. The u32 is the output string length.

The output will be all-zero/empty if not available - such as when this was used before DoHandshake*.

SslVersion

This is "nn::ssl::sf::SslVersion" or "nn::ssl::Context::SslVersion".

This is a bitmask which controls the min/max TLS versions to use, depending on which lowest/highest bits are set (if Auto isn't set).

Auto uses min=TlsV10 max=TlsV12.

[12.0.0+] Auto now uses min=TlsV10 max=((ApiVersion < 2) ? TlsV12 : TlsV13).

[12.0.3+] TLS 1.3 is no longer used. The TlsV13 bit is now handled the same as TlsV12 (uses TLS 1.2), and Auto only uses TLS 1.2 for the maximum.

Bits Description
0 Auto
3 TlsV10
4 TlsV11
5 TlsV12
6 [11.0.0+] TlsV13
24-31 [11.0.0+] ApiVersion

DebugOptionType

This is "nn::ssl::sf::DebugOptionType" or "nn::ssl::DebugOption".

Value Description
0 AllowDisableVerifyOption

FlushSessionCacheOptionType

This is "nn::ssl::sf::FlushSessionCacheOptionType" or "nn::ssl::FlushSessionCacheOptionType".

Value Description
0 SingleHost
1 AllHosts

BuiltInCertificateInfo

This is "nn::ssl::BuiltInManager::BuiltInCertificateInfo".

Offset Size Description
0x0 0x4 #CaCertificateId
0x4 0x4 #TrustedCertStatus
0x8 0x8 CertificateSize
0x10 0x8 CertificateDataOffset

This is the struct returned by #GetCertificates. It is internally converted from "nn::ssl::detail::BuiltinDataInfo" by copying "nn::ssl::detail::BuiltinDataInfo::BuiltinDataStatus" into #TrustedCertStatus and official software then further converts this to "nn::ssl::BuiltInManager::BuiltInCertificateInfo" by transforming "CertificateDataOffset" into an actual pointer.

TrustedCertStatus

This is "nn::ssl::TrustedCertStatus".

Value Description
-1 Invalid
0 Removed
1 EnabledTrusted
2 EnabledNotTrusted
3 Revoked

CaCertificateId

This is "nn::ssl::CaCertificateId".

Value Description
-1 [3.0.0+] All
1 NintendoCAG3
2 NintendoClass2CAG3
1000 AmazonRootCA1
1001 StarfieldServicesRootCertificateAuthorityG2
1002 AddTrustExternalCARoot
1003 COMODOCertificationAuthority
1004 UTNDATACorpSGC
1005 UTNUSERFirstHardware
1006 BaltimoreCyberTrustRoot
1007 CybertrustGlobalRoot
1008 VerizonGlobalRootCA
1009 DigiCertAssuredIDRootCA
1010 DigiCertAssuredIDRootG2
1011 DigiCertGlobalRootCA
1012 DigiCertGlobalRootG2
1013 DigiCertHighAssuranceEVRootCA
1014 EntrustnetCertificationAuthority2048
1015 EntrustRootCertificationAuthority
1016 EntrustRootCertificationAuthorityG2
1017 GeoTrustGlobalCA2 ([8.0.0+] #TrustedCertStatus is EnabledNotTrusted)
1018 GeoTrustGlobalCA ([8.0.0+] #TrustedCertStatus is EnabledNotTrusted)
1019 GeoTrustPrimaryCertificationAuthorityG3 ([8.0.0+] #TrustedCertStatus is EnabledNotTrusted)
1020 GeoTrustPrimaryCertificationAuthority ([8.0.0+] #TrustedCertStatus is EnabledNotTrusted)
1021 GlobalSignRootCA
1022 GlobalSignRootCAR2
1023 GlobalSignRootCAR3
1024 GoDaddyClass2CertificationAuthority
1025 GoDaddyRootCertificateAuthorityG2
1026 StarfieldClass2CertificationAuthority
1027 StarfieldRootCertificateAuthorityG2
1028 thawtePrimaryRootCAG3 ([8.0.0+] #TrustedCertStatus is EnabledNotTrusted)
1029 thawtePrimaryRootCA ([8.0.0+] #TrustedCertStatus is EnabledNotTrusted)
1030 VeriSignClass3PublicPrimaryCertificationAuthorityG3 ([8.0.0+] #TrustedCertStatus is EnabledNotTrusted)
1031 VeriSignClass3PublicPrimaryCertificationAuthorityG5 ([8.0.0+] #TrustedCertStatus is EnabledNotTrusted)
1032 VeriSignUniversalRootCertificationAuthority ([8.0.0+] #TrustedCertStatus is EnabledNotTrusted)
1033 [6.0.0+] DSTRootCAX3
1034 [10.0.3+] "USERTrust RSA Certification Authority"
1035 [10.1.0+] "ISRG Root X10"
1036 [10.1.0+] "USERTrust ECC Certification Authority"
1037 [10.1.0+] "COMODO RSA Certification Authority"
1038 [10.1.0+] "COMODO ECC Certification Authority"
1039 [11.0.0+] "Amazon Root CA 2"
1040 [11.0.0+] "Amazon Root CA 3"
1041 [11.0.0+] "Amazon Root CA 4"
1042 [11.0.0+] "DigiCert Assured ID Root G3"
1043 [11.0.0+] "DigiCert Global Root G3"
1044 [11.0.0+] "DigiCert Trusted Root G4"
1045 [11.0.0+] "Entrust Root Certification Authority - EC1"
1046 [11.0.0+] "Entrust Root Certification Authority - G4"
1047 [11.0.0+] "GlobalSign ECC Root CA - R4"
1048 [11.0.0+] "GlobalSign ECC Root CA - R5"
1049 [11.0.0+] "GlobalSign ECC Root CA - R6"
1050 [11.0.0+] "GTS Root R1"
1051 [11.0.0+] "GTS Root R2"
1052 [11.0.0+] "GTS Root R3"
1053 [11.0.0+] "GTS Root R4"
1054 [12.0.0+] "Security Communication RootCA"

InternalPki

This is "nn::ssl::sf::InternalPki" or "nn::ssl::Context::InternalPki".

Value Description
0 None
1 DeviceClientCertDefault

An error is thrown by #RegisterInternalPki when the input value does not match "DeviceClientCertDefault".

"DeviceClientCertDefault" enables using the DeviceCert.

ContextOption

This is "nn::ssl::sf::ContextOption" or "nn::ssl::Context::ContextOption".

The default value for CrlImportDateCheckEnable at the time of #ISslContext object creation is value 1.

Value Description
0 None
1 CrlImportDateCheckEnable

CertificateFormat

This is "nn::ssl::sf::CertificateFormat" or "nn::ssl::CertificateFormat".

Value Description
1 Pem
2 Der

VerifyOption

This is "nn::ssl::sf::VerifyOption". This is a bitmask. At the time of #ISslConnection object creation, the default value is 0x3.

Bit Description
0 PeerCa
1 HostName
2 DateCheck
3 EvCertPartial
4 [6.0.0+] EvPolicyOid
5 [6.0.0+] EvCertFingerprint

Originally ssl-sysmodule (#SetVerifyOption) just wrote the input field to state. With [5.0.0+] there's now validation for the input, with the value written to state masked with {allowed bitmask}. When InterfaceVersion is >=0x2, the low 2-bits of VerifyOption must be set, unless {state flag for SkipDefaultVerify} is set or [9.0.0+] {bool DebugOption state flag} is set, otherwise an error is thrown. [6.0.0+]: Following that, if VerifyOption bit4 is set, then VerifyOption & 0x15 must match 0x15 otherwise an error is thrown.

IoMode

This is "nn::ssl::sf::IoMode" or "nn::ssl::Connection::IoMode". At the time of #ISslConnection object creation, the default value is 1.

The socket non-blocking flag is always set regardless of this field, this is only used for calculating the timeout (converted from milliseconds) passed to the NSS funcs used by various cmds. NonBlocking = 0, Blocking = 300000 (5 minutes).

Value Description
1 Blocking
2 NonBlocking

PollEvent

This is "nn::ssl::sf::PollEvent" or "nn::ssl::Connection::PollEvent". This is a bitmask.

Bit Description
0 Read
1 Write
2 Except

SessionCacheMode

This is "nn::ssl::sf::SessionCacheMode" or "nn::ssl::Connection::SessionCacheMode".

Value Description
0 None
1 SessionId
2 SessionTicket

RenegotiationMode

This is "nn::ssl::sf::RenegotiationMode" or "nn::ssl::Connection::RenegotiationMode".

Value Description
0 None
1 Secure

OptionType

This is "nn::ssl::sf::OptionType" or "nn::ssl::Connection::OptionType".

Value Description
0 DoNotCloseSocket
1 [3.0.0+] GetServerCertChain
2 [5.0.0+] SkipDefaultVerify
3 [9.0.0+] EnableAlpn

This corresponds to bool flags. At the time of #ISslConnection object creation, all of these bool flags are cleared.

"SkipDefaultVerify" is checked by SetVerifyOption and "EnableAlpn" is only available with SetOption.

SetOption with "DoNotCloseSocket" is only available when #SetSocketDescriptor was not used previously. "EnableAlpn" can optionally use the state setup by #SetSocketDescriptor, but it will return 0 regardless.

See #SetNextAlpnProto for "EnableAlpn", and #DoHandshakeGetServerCert for "GetServerCertChain".

AlpnProtoState

This is "nn::ssl::sf::AlpnProtoState" or "nn::ssl::Connection::AlpnProtoState".

Value Description
0 NoSupport
1 Negotiated
2 NoOverlap
3 Selected
4 EarlyValue

CipherInfo

This is "nn::ssl::Connection::CipherInfo". This is a 0x48-byte struct.

Offset Size Description
0x0 0x40 Cipher string
0x40 0x8 Protocol version string

CertStore

This is the CertStore title, which contains the following files in RomFS:

  • "/ssl_TrustedCerts.bdf" ([1.0.0-2.3.0] "ssl_TrustedCerts.tcf") (file was renamed with [3.0.0], content is identical)
  • [3.0.0+] "/ssl_Crl.bdf"
  • [6.0.0+] "/ssl_CaFingerprints.bdf"

The content of this SystemData was updated with the following system-versions: 3.0.0, 6.0.0, 8.0.0, 10.1.0, 11.0.0, 12.0.0. See #CaCertificateId for the ssl_TrustedCerts changes.

[10.1.0+] added 3 more fingerprints to "/ssl_CaFingerprints.bdf".

[11.0.0+] updated "/ssl_CaFingerprints.bdf" and "/ssl_TrustedCerts.bdf".

[12.0.0+] updated "/ssl_TrustedCerts.bdf".

#ISslContext automatically uses this CertStore, regardless of the used cmds.

These have the following structure:

Offset Size Description
0x0 0x4 Magic "sslT"
0x4 0x4 Total entries
0x8 0x10*{total entries} Array entries

Array entry structure:

Offset Size Description
0x0 0x4 Id
0x4 0x4 #TrustedCertStatus
0x8 0x4 Data size
0xC 0x4 Data offset

Data offset is relative to absolute offset 0x8.

The Id is the same one used by service commands to access these entries. For ssl_TrustedCerts, Id is #CaCertificateId.

Client cert+privk

SSL-sysmodule uses set:cal GetSslKey and GetSslCert. The rest of this section documents handling for the former, which can be decrypted with SPL.

key* below refers to the 3 0x10-byte input blocks passed to this code.

When actual_size is:

  • 0x100+0x10: If the u32 actual_size is less than (u32)-0x11, and the last 0x10-bytes of the actual-data are all-zero, the data is copied to the output as raw plaintext. If a non-zero byte is found, it will continue with SPL usage, skipping over the SPL block for the devunit flag. In this case, key=key0 and the flag passed to SPL later is set to 0.
  • 0x100+0x30: Size must match this if it's not the above, otherwise error 0xC81A is returned. The flag passed to SPL later is set to 1 in this case. Runs the devunit-flag-block: uses SPL_services#SPL#GetDevunitFlag. key = key1 when out_flag!=0, key2 otherwise.