*/
#pragma once
-/// HTTP context.
+/// sslc context.
typedef struct {
Handle servhandle; ///< Service handle.
u32 sslchandle; ///< SSLC handle.
} sslcContext;
+typedef enum {
+ SSLC_DefaultRootCert_Nintendo_CA = 0x1, //"Nintendo CA"
+ SSLC_DefaultRootCert_Nintendo_CA_G2 = 0x2, //"Nintendo CA - G2"
+ SSLC_DefaultRootCert_Nintendo_CA_G3 = 0x3, //"Nintendo CA - G3"
+ SSLC_DefaultRootCert_Nintendo_Class2_CA = 0x4, //"Nintendo Class 2 CA"
+ SSLC_DefaultRootCert_Nintendo_Class2_CA_G2 = 0x5, //"Nintendo Class 2 CA - G2"
+ SSLC_DefaultRootCert_Nintendo_Class2_CA_G3 = 0x6, //"Nintendo Class 2 CA - G3"
+ SSLC_DefaultRootCert_CyberTrust = 0x7, //"GTE CyberTrust Global Root"
+ SSLC_DefaultRootCert_AddTrust_External_CA = 0x8, //"AddTrust External CA Root"
+ SSLC_DefaultRootCert_COMODO = 0x9, //"COMODO RSA Certification Authority"
+ SSLC_DefaultRootCert_USERTrust = 0xA, //"USERTrust RSA Certification Authority"
+ SSLC_DefaultRootCert_DigiCert_EV = 0xB //"DigiCert High Assurance EV Root CA"
+} SSLC_DefaultRootCert;
+
+typedef enum {
+ SSLC_DefaultClientCert_ClCertA = 0x40
+} SSLC_DefaultClientCert;
+
/// Initializes SSLC. Normally session_handle should be 0. When non-zero this will use the specified handle for the main-service-session without using the Initialize command, instead of using srvGetServiceHandle.
Result sslcInit(Handle session_handle);
/// Exits SSLC.
void sslcExit(void);
+/**
+ * @brief Creates a RootCertChain.
+ * @param RootCertChain_contexthandle Output contexthandle.
+ */
+Result sslcCreateRootCertChain(u32 *RootCertChain_contexthandle);
+
+/**
+ * @brief Destroys a RootCertChain.
+ * @param RootCertChain_contexthandle RootCertChain contexthandle.
+ */
+Result sslcDestroyRootCertChain(u32 RootCertChain_contexthandle);
+
/**
* @brief Adds a trusted RootCA cert to a RootCertChain.
* @param RootCertChain_contexthandle RootCertChain to use.
- * @param cert Pointer to DER cert.
+ * @param cert Pointer to the DER cert.
* @param certsize Size of the DER cert.
*/
-Result sslcAddTrustedRootCA(u32 RootCertChain_contexthandle, u8 *cert, u32 certsize);
+Result sslcAddTrustedRootCA(u32 RootCertChain_contexthandle, u8 *cert, u32 certsize, u32 *cert_contexthandle);
+
+/**
+ * @brief Adds a default RootCA cert to a RootCertChain.
+ * @param RootCertChain_contexthandle RootCertChain to use.
+ * @param certID ID of the cert to add.
+ * @param cert_contexthandle Optional, the cert contexthandle can be written here.
+ */
+Result sslcRootCertChainAddDefaultCert(u32 RootCertChain_contexthandle, SSLC_DefaultRootCert certID, u32 *cert_contexthandle);
+
+/**
+ * @brief Removes the specified cert from the RootCertChain.
+ * @param RootCertChain_contexthandle RootCertChain to use.
+ * @param cert_contexthandle Cert contexthandle to remove from the RootCertChain.
+ */
+Result sslcRootCertChainRemoveCert(u32 RootCertChain_contexthandle, u32 cert_contexthandle);
+
+/**
+ * @brief Opens a new ClientCert-context.
+ * @param cert Pointer to the DER cert.
+ * @param certsize Size of the DER cert.
+ * @param key Pointer to the DER key.
+ * @param keysize Size of the DER key.
+ * @param ClientCert_contexthandle Output contexthandle.
+ */
+Result sslcOpenClientCertContext(u8 *cert, u32 certsize, u8 *key, u32 keysize, u32 *ClientCert_contexthandle);
+
+/**
+ * @brief Opens a ClientCert-context with a default certID.
+ * @param certID ID of the ClientCert to use.
+ * @param ClientCert_contexthandle Output contexthandle.
+ */
+Result sslcOpenDefaultClientCertContext(SSLC_DefaultClientCert certID, u32 *ClientCert_contexthandle);
+
+/**
+ * @brief Closes the specified ClientCert-context.
+ * @param ClientCert_contexthandle ClientCert-context to use.
+ */
+Result sslcCloseClientCertContext(u32 ClientCert_contexthandle);
+
+/**
+ * @brief This uses ps:ps SeedRNG internally.
+ */
+Result sslcSeedRNG(void);
+
+/**
+ * @brief This uses ps:ps GenerateRandomData internally.
+ * @param buf Output buffer.
+ * @param size Output size.
+ */
+Result sslcGenerateRandomData(u8 *buf, u32 size);
+
+/**
+ * @brief Creates a sslc context.
+ * @param context sslc context.
+ * @param sockfd Socket fd, this code automatically uses the required SOC command before using the actual sslc command.
+ * @param input_opt Input sslc options bitmask. The default value for this param is 0.
+ * @param hostname Server hostname.
+ */
+Result sslcCreateContext(sslcContext *context, int sockfd, u32 input_opt, char *hostname);
+
+/*
+ * @brief Destroys a sslc context. The associated sockfd must be closed manually.
+ * @param context sslc context.
+ */
+Result sslcDestroyContext(sslcContext *context);
+
+/*
+ * @brief Starts the TLS connection. If successful, this will not return until the connection is ready for data-transfer via sslcRead/sslcWrite.
+ * @param context sslc context.
+ * @param internal_retval Optional ptr where the internal_retval will be written. The value is only copied to here by this function when no error occurred.
+ * @param out Optional ptr where an output u32 will be written. The value is only copied to here by this function when no error occurred.
+ */
+Result sslcStartConnection(sslcContext *context, int *internal_retval, u32 *out);
+
+/*
+ * @brief Receive data over the network connection.
+ * @param context sslc context.
+ * @param buf Output buffer.
+ * @param len Size to receive.
+ * @param peek When set, this is equivalent to setting the recv() MSG_PEEK flag.
+ * @return When this isn't an error-code, this is the total transferred data size.
+ */
+Result sslcRead(sslcContext *context, void *buf, size_t len, bool peek);
+
+/*
+ * @brief Send data over the network connection.
+ * @param context sslc context.
+ * @param buf Input buffer.
+ * @param len Size to send.
+ * @return When this isn't an error-code, this is the total transferred data size.
+ */
+Result sslcWrite(sslcContext *context, void *buf, size_t len);
+
+/*
+ * @brief Set the RootCertChain for the specified sslc context.
+ * @param context sslc context.
+ * @param handle RootCertChain contexthandle.
+ */
+Result sslcContextSetRootCertChain(sslcContext *context, u32 handle);
+
+/*
+ * @brief Set the ClientCert-context for the specified sslc context.
+ * @param context sslc context.
+ * @param handle ClientCert contexthandle.
+ */
+Result sslcContextSetClientCert(sslcContext *context, u32 handle);
+
+/*
+ * @brief Set the context which was created by command 0x00080000, for the specified sslc context. This needs updated once it's known what this context is for.
+ * @param context sslc context.
+ * @param handle contexthandle.
+ */
+Result sslcContextSetHandle8(sslcContext *context, u32 handle);
+
+/*
+ * @brief Clears the options field bits for the context using the specified bitmask.
+ * @param context sslc context.
+ * @param bitmask opt bitmask.
+ */
+Result sslcContextClearOpt(sslcContext *context, u32 bitmask);
+
+/*
+ * @brief This loads an u32 from the specified context state. This needs updated once it's known what this field is for.
+ * @param context sslc context.
+ * @param out Output ptr to write the value to.
+ */
+Result sslcContextGetState(sslcContext *context, u32 *out);
#include <3ds/services/sslc.h>
#include <3ds/ipc.h>
+#include "soc/soc_common.h"
+
Handle __sslc_servhandle;
static int __sslc_refcount;
-Result SSLC_Initialize(void);
+static Result sslcipc_Initialize(void);
Result sslcInit(Handle session_handle)
{
__sslc_servhandle = session_handle;
if(__sslc_servhandle==0)ret = srvGetServiceHandle(&__sslc_servhandle, "ssl:C");
- if(session_handle==0 && R_SUCCEEDED(ret))ret = SSLC_Initialize();
+ if(session_handle==0 && R_SUCCEEDED(ret))ret = sslcipc_Initialize();
if (R_FAILED(ret)) AtomicDecrement(&__sslc_refcount);
return ret;
svcCloseHandle(__sslc_servhandle);
}
-Result SSLC_Initialize(void)
+static Result sslcipc_Initialize(void)
{
u32* cmdbuf=getThreadCommandBuffer();
return cmdbuf[1];
}
-Result sslcAddTrustedRootCA(u32 RootCertChain_contexthandle, u8 *cert, u32 certsize)
+static Result sslcipc_CreateContext(sslcContext *context, int sockfd, u32 input_opt, char *hostname)
+{
+ u32* cmdbuf=getThreadCommandBuffer();
+ u32 size = strlen(hostname)+1;
+
+ cmdbuf[0]=IPC_MakeHeader(0x2,3,2); // 0x200C2
+ cmdbuf[1]=(u32)sockfd;
+ cmdbuf[2]=input_opt;
+ cmdbuf[3]=size;
+ cmdbuf[4]=IPC_Desc_Buffer(size, IPC_BUFFER_R);
+ cmdbuf[5]=(u32)hostname;
+
+ Result ret=0;
+ if(R_FAILED(ret=svcSendSyncRequest(__sslc_servhandle)))return ret;
+ ret = cmdbuf[1];
+
+ if(R_SUCCEEDED(ret))context->sslchandle = cmdbuf[2];
+
+ return ret;
+}
+
+Result sslcCreateRootCertChain(u32 *RootCertChain_contexthandle)
+{
+ u32* cmdbuf=getThreadCommandBuffer();
+
+ cmdbuf[0]=IPC_MakeHeader(0x3,0,0); // 0x30000
+
+ Result ret=0;
+ if(R_FAILED(ret=svcSendSyncRequest(__sslc_servhandle)))return ret;
+ ret = cmdbuf[1];
+
+ if(R_SUCCEEDED(ret))*RootCertChain_contexthandle = cmdbuf[2];
+
+ return ret;
+}
+
+Result sslcDestroyRootCertChain(u32 RootCertChain_contexthandle)
+{
+ u32* cmdbuf=getThreadCommandBuffer();
+
+ cmdbuf[0]=IPC_MakeHeader(0x4,1,0); // 0x40040
+ cmdbuf[1]=RootCertChain_contexthandle;
+
+ Result ret=0;
+ if(R_FAILED(ret=svcSendSyncRequest(__sslc_servhandle)))return ret;
+
+ return cmdbuf[1];
+}
+
+Result sslcAddTrustedRootCA(u32 RootCertChain_contexthandle, u8 *cert, u32 certsize, u32 *cert_contexthandle)
{
u32* cmdbuf=getThreadCommandBuffer();
cmdbuf[3]=IPC_Desc_Buffer(certsize, IPC_BUFFER_R);
cmdbuf[4]=(u32)cert;
+ Result ret=0;
+ if(R_FAILED(ret=svcSendSyncRequest(__sslc_servhandle)))return ret;
+ ret = cmdbuf[1];
+
+ if(R_SUCCEEDED(ret) && cert_contexthandle)*cert_contexthandle = cmdbuf[2];
+
+ return ret;
+}
+
+Result sslcRootCertChainAddDefaultCert(u32 RootCertChain_contexthandle, SSLC_DefaultRootCert certID, u32 *cert_contexthandle)
+{
+ u32* cmdbuf=getThreadCommandBuffer();
+
+ cmdbuf[0]=IPC_MakeHeader(0x6,2,0); // 0x60080
+ cmdbuf[1]=RootCertChain_contexthandle;
+ cmdbuf[2]=certID;
+
+ Result ret=0;
+ if(R_FAILED(ret=svcSendSyncRequest(__sslc_servhandle)))return ret;
+ ret = cmdbuf[1];
+
+ if(R_SUCCEEDED(ret) && cert_contexthandle)*cert_contexthandle = cmdbuf[2];
+
+ return ret;
+}
+
+Result sslcRootCertChainRemoveCert(u32 RootCertChain_contexthandle, u32 cert_contexthandle)
+{
+ u32* cmdbuf=getThreadCommandBuffer();
+
+ cmdbuf[0]=IPC_MakeHeader(0x7,2,0); // 0x70080
+ cmdbuf[1]=RootCertChain_contexthandle;
+ cmdbuf[2]=cert_contexthandle;
+
+ Result ret=0;
+ if(R_FAILED(ret=svcSendSyncRequest(__sslc_servhandle)))return ret;
+
+ return cmdbuf[1];
+}
+
+Result sslcOpenClientCertContext(u8 *cert, u32 certsize, u8 *key, u32 keysize, u32 *ClientCert_contexthandle)
+{
+ u32* cmdbuf=getThreadCommandBuffer();
+
+ cmdbuf[0]=IPC_MakeHeader(0xD,2,4); // 0xD0084
+ cmdbuf[1]=certsize;
+ cmdbuf[2]=keysize;
+ cmdbuf[3]=IPC_Desc_Buffer(certsize, IPC_BUFFER_R);
+ cmdbuf[4]=(u32)cert;
+ cmdbuf[5]=IPC_Desc_Buffer(keysize, IPC_BUFFER_R);
+ cmdbuf[6]=(u32)key;
+
+ Result ret=0;
+ if(R_FAILED(ret=svcSendSyncRequest(__sslc_servhandle)))return ret;
+ ret = cmdbuf[1];
+
+ if(R_SUCCEEDED(ret))*ClientCert_contexthandle = cmdbuf[2];
+
+ return ret;
+}
+
+Result sslcOpenDefaultClientCertContext(SSLC_DefaultClientCert certID, u32 *ClientCert_contexthandle)
+{
+ u32* cmdbuf=getThreadCommandBuffer();
+
+ cmdbuf[0]=IPC_MakeHeader(0xE,1,0); // 0xE0040
+ cmdbuf[1]=certID;
+
+ Result ret=0;
+ if(R_FAILED(ret=svcSendSyncRequest(__sslc_servhandle)))return ret;
+ ret = cmdbuf[1];
+
+ if(R_SUCCEEDED(ret))*ClientCert_contexthandle = cmdbuf[2];
+
+ return ret;
+}
+
+Result sslcCloseClientCertContext(u32 ClientCert_contexthandle)
+{
+ u32* cmdbuf=getThreadCommandBuffer();
+
+ cmdbuf[0]=IPC_MakeHeader(0xF,1,0); // 0xF0040
+ cmdbuf[1]=ClientCert_contexthandle;
+
+ Result ret=0;
+ if(R_FAILED(ret=svcSendSyncRequest(__sslc_servhandle)))return ret;
+
+ return cmdbuf[1];
+}
+
+Result sslcSeedRNG(void)
+{
+ u32* cmdbuf=getThreadCommandBuffer();
+
+ cmdbuf[0]=IPC_MakeHeader(0x10,0,0); // 0x100000
+
+ Result ret=0;
+ if(R_FAILED(ret=svcSendSyncRequest(__sslc_servhandle)))return ret;
+
+ return cmdbuf[1];
+}
+
+Result sslcGenerateRandomData(u8 *buf, u32 size)
+{
+ u32* cmdbuf=getThreadCommandBuffer();
+
+ cmdbuf[0]=IPC_MakeHeader(0x11,1,2); // 0x110042
+ cmdbuf[1]=size;
+ cmdbuf[2]=IPC_Desc_Buffer(size, IPC_BUFFER_W);
+ cmdbuf[3]=(u32)buf;
+
+ Result ret=0;
+ if(R_FAILED(ret=svcSendSyncRequest(__sslc_servhandle)))return ret;
+
+ return cmdbuf[1];
+}
+
+static Result sslcipc_InitializeConnectionSession(sslcContext *context)
+{
+ u32* cmdbuf=getThreadCommandBuffer();
+
+ cmdbuf[0]=IPC_MakeHeader(0x12,1,2); // 0x120042
+ cmdbuf[1]=context->sslchandle;
+ cmdbuf[2]=IPC_Desc_CurProcessHandle();
+
+ Result ret=0;
+ if(R_FAILED(ret=svcSendSyncRequest(context->servhandle)))return ret;
+
+ return cmdbuf[1];
+}
+
+static Result sslcipc_StartConnection(sslcContext *context)
+{
+ u32* cmdbuf=getThreadCommandBuffer();
+
+ cmdbuf[0]=IPC_MakeHeader(0x13,1,0); // 0x130040
+ cmdbuf[1]=context->sslchandle;
+
+ Result ret=0;
+ if(R_FAILED(ret=svcSendSyncRequest(context->servhandle)))return ret;
+
+ return cmdbuf[1];
+}
+
+static Result sslcipc_StartConnectionGetOut(sslcContext *context, int *internal_retval, u32 *out)
+{
+ u32* cmdbuf=getThreadCommandBuffer();
+
+ cmdbuf[0]=IPC_MakeHeader(0x14,1,0); // 0x140040
+ cmdbuf[1]=context->sslchandle;
+
+ Result ret=0;
+ if(R_FAILED(ret=svcSendSyncRequest(context->servhandle)))return ret;
+ ret = cmdbuf[1];
+
+ if(R_SUCCEEDED(ret))
+ {
+ if(internal_retval)*internal_retval = cmdbuf[2];
+ if(out)*out = cmdbuf[3];
+ }
+
+ return ret;
+}
+
+static Result sslcipc_DataTransfer(sslcContext *context, void *buf, size_t len, u32 type)
+{
+ u32* cmdbuf=getThreadCommandBuffer();
+
+ if(type >= 3)return -1;
+
+ cmdbuf[0]=IPC_MakeHeader(0x15 + type,2,2); // 0x150082
+ cmdbuf[1]=context->sslchandle;
+ cmdbuf[2]=len;
+ if(type<2)cmdbuf[3]=IPC_Desc_Buffer(len, IPC_BUFFER_W);
+ if(type==2)cmdbuf[3]=IPC_Desc_Buffer(len, IPC_BUFFER_R);
+ cmdbuf[4]=(u32)buf;
+
+ Result ret=0;
+ if(R_FAILED(ret=svcSendSyncRequest(context->servhandle)))return ret;
+ ret = cmdbuf[1];
+
+ if(R_SUCCEEDED(ret))ret = cmdbuf[2];
+
+ return ret;
+}
+
+static Result sslcipc_ContextSetValue(sslcContext *context, u32 type, u32 value)
+{
+ u32* cmdbuf=getThreadCommandBuffer();
+
+ if(type >= 4)return -1;
+
+ cmdbuf[0]=IPC_MakeHeader(0x18 + type,2,0); // 0x180080
+ cmdbuf[1]=context->sslchandle;
+ cmdbuf[2]=value;
+
+ Result ret=0;
+ if(R_FAILED(ret=svcSendSyncRequest(context->servhandle)))return ret;
+
+ return cmdbuf[1];
+}
+
+Result sslcContextGetState(sslcContext *context, u32 *out)
+{
+ u32* cmdbuf=getThreadCommandBuffer();
+
+ cmdbuf[0]=IPC_MakeHeader(0x1D,1,0); // 0x1D0040
+ cmdbuf[1]=context->sslchandle;
+
+ Result ret=0;
+ if(R_FAILED(ret=svcSendSyncRequest(context->servhandle)))return ret;
+ ret = cmdbuf[1];
+
+ if(R_SUCCEEDED(ret))*out = cmdbuf[2];
+
+ return ret;
+}
+
+static Result sslcipc_DestroyContext(sslcContext *context)
+{
+ u32* cmdbuf=getThreadCommandBuffer();
+
+ cmdbuf[0]=IPC_MakeHeader(0x1E,1,0); // 0x1E0040
+ cmdbuf[1]=context->sslchandle;
+
Result ret=0;
if(R_FAILED(ret=svcSendSyncRequest(__sslc_servhandle)))return ret;
return cmdbuf[1];
}
+Result sslcCreateContext(sslcContext *context, int sockfd, u32 input_opt, char *hostname)
+{
+ Result ret=0;
+
+ ret = SOCU_AddGlobalSocket(sockfd);
+ if(R_FAILED(ret))return ret;
+
+ sockfd = soc_get_fd(sockfd);
+ if(sockfd < 0) {
+ errno = -sockfd;
+ return -1;
+ }
+
+ ret = sslcipc_CreateContext(context, sockfd, input_opt, hostname);
+ if(R_FAILED(ret))return ret;
+
+ ret = srvGetServiceHandle(&context->servhandle, "ssl:C");
+ if(R_FAILED(ret)) {
+ sslcipc_DestroyContext(context);
+ return ret;
+ }
+
+ ret = sslcipc_InitializeConnectionSession(context);
+ if(R_FAILED(ret)) {
+ svcCloseHandle(context->servhandle);
+ sslcipc_DestroyContext(context);
+ }
+
+ return ret;
+}
+
+Result sslcDestroyContext(sslcContext *context)
+{
+ Result ret=0;
+
+ svcCloseHandle(context->servhandle);
+ ret = sslcipc_DestroyContext(context);
+
+ return ret;
+}
+
+Result sslcStartConnection(sslcContext *context, int *internal_retval, u32 *out)
+{
+ if(internal_retval || out)return sslcipc_StartConnectionGetOut(context, internal_retval, out);
+ return sslcipc_StartConnection(context);
+}
+
+Result sslcRead(sslcContext *context, void *buf, size_t len, bool peek)
+{
+ return sslcipc_DataTransfer(context, buf, len, peek);
+}
+
+Result sslcWrite(sslcContext *context, void *buf, size_t len)
+{
+ return sslcipc_DataTransfer(context, buf, len, 2);
+}
+
+Result sslcContextSetRootCertChain(sslcContext *context, u32 handle)
+{
+ return sslcipc_ContextSetValue(context, 0, handle);
+}
+
+Result sslcContextSetClientCert(sslcContext *context, u32 handle)
+{
+ return sslcipc_ContextSetValue(context, 1, handle);
+}
+
+Result sslcContextSetHandle8(sslcContext *context, u32 handle)
+{
+ return sslcipc_ContextSetValue(context, 2, handle);
+}
+
+Result sslcContextClearOpt(sslcContext *context, u32 bitmask)
+{
+ return sslcipc_ContextSetValue(context, 3, bitmask);
+}
+