Implemented actual support for sslc, homebrew can now actually use sslc(see also the sslc example).

This commit is contained in:
yellows8 2016-03-30 13:57:20 -04:00
parent b7b78414da
commit 3d90087481
2 changed files with 517 additions and 7 deletions

View File

@ -4,23 +4,180 @@
*/ */
#pragma once #pragma once
/// HTTP context. /// sslc context.
typedef struct { typedef struct {
Handle servhandle; ///< Service handle. Handle servhandle; ///< Service handle.
u32 sslchandle; ///< SSLC handle. u32 sslchandle; ///< SSLC handle.
} sslcContext; } 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. /// 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); Result sslcInit(Handle session_handle);
/// Exits SSLC. /// Exits SSLC.
void sslcExit(void); 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. * @brief Adds a trusted RootCA cert to a RootCertChain.
* @param RootCertChain_contexthandle RootCertChain to use. * @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. * @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);

View File

@ -9,10 +9,12 @@
#include <3ds/services/sslc.h> #include <3ds/services/sslc.h>
#include <3ds/ipc.h> #include <3ds/ipc.h>
#include "soc/soc_common.h"
Handle __sslc_servhandle; Handle __sslc_servhandle;
static int __sslc_refcount; static int __sslc_refcount;
Result SSLC_Initialize(void); static Result sslcipc_Initialize(void);
Result sslcInit(Handle session_handle) Result sslcInit(Handle session_handle)
{ {
@ -23,7 +25,7 @@ Result sslcInit(Handle session_handle)
__sslc_servhandle = session_handle; __sslc_servhandle = session_handle;
if(__sslc_servhandle==0)ret = srvGetServiceHandle(&__sslc_servhandle, "ssl:C"); 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); if (R_FAILED(ret)) AtomicDecrement(&__sslc_refcount);
return ret; return ret;
@ -36,7 +38,7 @@ void sslcExit(void)
svcCloseHandle(__sslc_servhandle); svcCloseHandle(__sslc_servhandle);
} }
Result SSLC_Initialize(void) static Result sslcipc_Initialize(void)
{ {
u32* cmdbuf=getThreadCommandBuffer(); u32* cmdbuf=getThreadCommandBuffer();
@ -49,7 +51,56 @@ Result SSLC_Initialize(void)
return cmdbuf[1]; 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(); 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[3]=IPC_Desc_Buffer(certsize, IPC_BUFFER_R);
cmdbuf[4]=(u32)cert; 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; Result ret=0;
if(R_FAILED(ret=svcSendSyncRequest(__sslc_servhandle)))return ret; if(R_FAILED(ret=svcSendSyncRequest(__sslc_servhandle)))return ret;
return cmdbuf[1]; 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);
}