]> Chaos Git - corbenik/ctrulib.git/commitdiff
Implemented actual support for sslc, homebrew can now actually use sslc(see also...
authoryellows8 <yellows8@users.noreply.github.com>
Wed, 30 Mar 2016 17:57:20 +0000 (13:57 -0400)
committeryellows8 <yellows8@users.noreply.github.com>
Wed, 30 Mar 2016 17:57:20 +0000 (13:57 -0400)
libctru/include/3ds/services/sslc.h
libctru/source/services/sslc.c

index b603507c610eed90b3df3c21c201ea65f666dadd..ca6f9cdb4d46c0097c735776b899ae6d5aa10af8 100644 (file)
  */
 #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);
 
index 2fe0044832f44e388dbac69388f8c070b734ebc6..99a90d862dd5e7ce4e25bc6561329e2b66f862e5 100644 (file)
@@ -9,10 +9,12 @@
 #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)
 {
@@ -23,7 +25,7 @@ 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;
@@ -36,7 +38,7 @@ void sslcExit(void)
        svcCloseHandle(__sslc_servhandle);
 }
 
-Result SSLC_Initialize(void)
+static Result sslcipc_Initialize(void)
 {
        u32* cmdbuf=getThreadCommandBuffer();
 
@@ -49,7 +51,56 @@ Result SSLC_Initialize(void)
        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();
 
@@ -59,9 +110,311 @@ Result sslcAddTrustedRootCA(u32 RootCertChain_contexthandle, u8 *cert, u32 certs
        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);
+}
+