-
Notifications
You must be signed in to change notification settings - Fork 513
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Unitytls integration #784
Unitytls integration #784
Changes from 63 commits
d773c76
90d29d7
5ce647e
b1e0d68
4d49d8b
8f1c80b
5b0f3c4
1b25fe8
598e0d4
98e940d
0b84cf2
74d5592
f7dac9e
ad45770
f77b0c5
f6c2b75
cfa1e04
445e584
594e2ac
ca57238
af91c35
5bb3eb6
9ea74ae
22b6ae9
6ecb133
32ca087
af58b51
4a0611c
d282ba9
01d462a
6492173
a898096
048d08a
7f65773
86c910f
7cf1799
3e58282
bc25f62
afc9bb2
d303e41
2cb0c4c
9bd3b1a
dbe0ae8
936a925
f938941
39be2ef
bf391b2
e7671c1
3b9f52d
f9c524f
9d07185
1382d28
62e7a52
b990700
a47260d
b1a8eca
589f130
ca2058f
0a1e777
d90ff2f
f9e79da
9889bec
a5df54c
fe8bf68
1165e17
8f7503b
02aa179
3843d8e
601111c
52e347b
65af3e7
5a28379
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,53 @@ | ||
#if SECURITY_DEP | ||
using System.Security.Cryptography.X509Certificates; | ||
|
||
namespace Mono.Unity | ||
{ | ||
internal unsafe static class CertHelper | ||
{ | ||
public static void AddCertificatesToNativeChain (UnityTls.unitytls_x509list* nativeCertificateChain, X509CertificateCollection certificates, UnityTls.unitytls_errorstate* errorState) | ||
{ | ||
foreach (var certificate in certificates) { | ||
AddCertificateToNativeChain (nativeCertificateChain, certificate, errorState); | ||
} | ||
} | ||
|
||
public static void AddCertificateToNativeChain (UnityTls.unitytls_x509list* nativeCertificateChain, X509Certificate certificate, UnityTls.unitytls_errorstate* errorState) | ||
{ | ||
byte[] certDer = certificate.GetRawCertData (); | ||
fixed(byte* certDerPtr = certDer) { | ||
UnityTls.NativeInterface.unitytls_x509list_append_der (nativeCertificateChain, certDerPtr, certDer.Length, errorState); | ||
} | ||
|
||
var certificateImpl2 = certificate.Impl as X509Certificate2Impl; | ||
if (certificateImpl2 != null) { | ||
var intermediates = certificateImpl2.IntermediateCertificates; | ||
if (intermediates != null && intermediates.Count > 0) { | ||
for (int i=0; i<intermediates.Count; ++i) { | ||
AddCertificateToNativeChain (nativeCertificateChain, new X509Certificate (intermediates[i]), errorState); | ||
} | ||
} | ||
} | ||
} | ||
|
||
public static X509CertificateCollection NativeChainToManagedCollection (UnityTls.unitytls_x509list_ref nativeCertificateChain, UnityTls.unitytls_errorstate* errorState) | ||
{ | ||
X509CertificateCollection certificates = new X509CertificateCollection (); | ||
|
||
var cert = UnityTls.NativeInterface.unitytls_x509list_get_x509 (nativeCertificateChain, 0, errorState); | ||
for (int i = 0; cert.handle != UnityTls.NativeInterface.UNITYTLS_INVALID_HANDLE; ++i) { | ||
size_t certBufferSize = UnityTls.NativeInterface.unitytls_x509_export_der (cert, null, 0, errorState); | ||
var certBuffer = new byte[certBufferSize]; // Need to reallocate every time since X509Certificate constructor takes no length but only a byte array. | ||
fixed(byte* certBufferPtr = certBuffer) { | ||
UnityTls.NativeInterface.unitytls_x509_export_der (cert, certBufferPtr, certBufferSize, errorState); | ||
} | ||
certificates.Add (new X509Certificate (certBuffer)); | ||
|
||
cert = UnityTls.NativeInterface.unitytls_x509list_get_x509 (nativeCertificateChain, i, errorState); | ||
} | ||
|
||
return certificates; | ||
} | ||
} | ||
} | ||
#endif |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
#if SECURITY_DEP | ||
#if MONO_SECURITY_ALIAS | ||
extern alias MonoSecurity; | ||
#endif | ||
|
||
#if MONO_SECURITY_ALIAS | ||
using MonoSecurity::Mono.Security.Interface; | ||
#else | ||
using Mono.Security.Interface; | ||
#endif | ||
|
||
namespace Mono.Unity | ||
{ | ||
internal static class Debug | ||
{ | ||
public static void CheckAndThrow (UnityTls.unitytls_errorstate errorState, string context, AlertDescription defaultAlert = AlertDescription.InternalError) | ||
{ | ||
if (errorState.code == UnityTls.unitytls_error_code.UNITYTLS_SUCCESS) | ||
return; | ||
|
||
string message = string.Format ("{0} - error code: {1}", context, errorState.code); | ||
throw new TlsException (defaultAlert, message); | ||
} | ||
|
||
public static void CheckAndThrow(UnityTls.unitytls_errorstate errorState, UnityTls.unitytls_x509verify_result verifyResult, string context, AlertDescription defaultAlert = AlertDescription.InternalError) | ||
{ | ||
// Ignore verify result if verification is not the issue. | ||
if (verifyResult == UnityTls.unitytls_x509verify_result.UNITYTLS_X509VERIFY_SUCCESS) { | ||
CheckAndThrow (errorState, context, defaultAlert); | ||
return; | ||
} | ||
|
||
AlertDescription alert = UnityTlsConversions.VerifyResultToAlertDescription (verifyResult, defaultAlert); | ||
string message = string.Format ("{0} - error code: {1}, verify result: {2}", context, errorState.code, verifyResult); | ||
throw new TlsException (alert, message); | ||
} | ||
} | ||
} | ||
#endif |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,249 @@ | ||
using System; | ||
using System.Runtime.InteropServices; | ||
using System.Runtime.CompilerServices; | ||
|
||
namespace Mono.Unity | ||
{ | ||
using UInt8 = Byte; | ||
using Int8 = Byte; | ||
|
||
[StructLayout (LayoutKind.Sequential)] | ||
internal struct size_t | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Not blocking, but does this There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It's mostly about the implicit cast to and from int. Since size_t is used for buffer sizes / array length, it would require quite a few explicit casts |
||
{ | ||
public size_t(uint i) { | ||
value = new IntPtr(i); | ||
} | ||
|
||
public static implicit operator size_t(int d) { | ||
return new size_t((uint)d); | ||
} | ||
public static implicit operator int(size_t s) { | ||
return s.value.ToInt32(); | ||
} | ||
|
||
public IntPtr value; | ||
} | ||
|
||
unsafe internal static partial class UnityTls | ||
{ | ||
private const string DLLNAME = "MacStandalonePlayer_TLSModule_Dynamic.dylib"; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. leftover |
||
private const CallingConvention CALLCONV = CallingConvention.Cdecl; | ||
|
||
// ------------------------------------ | ||
// Error Handling | ||
// ------------------------------------ | ||
public enum unitytls_error_code : UInt32 | ||
{ | ||
UNITYTLS_SUCCESS = 0, | ||
UNITYTLS_INVALID_ARGUMENT, // One of the arguments has an invalid value (e.g. null where not allowed) | ||
UNITYTLS_INVALID_FORMAT, // The passed data does not have a valid format. | ||
UNITYTLS_INVALID_STATE, // The object operating being operated on is not in a state that allows this function call. | ||
UNITYTLS_BUFFER_OVERFLOW, // A passed buffer was not large enough. | ||
UNITYTLS_OUT_OF_MEMORY, // Out of memory error | ||
UNITYTLS_INTERNAL_ERROR, // public implementation error. | ||
UNITYTLS_NOT_SUPPORTED, // The requested action is not supported on the current platform/implementation. | ||
UNITYTLS_ENTROPY_SOURCE_FAILED, // Failed to generate requested amount of entropy data. | ||
|
||
UNITYTLS_USER_WOULD_BLOCK, // Can be set by the user to signal that a call (e.g. read/write callback) would block and needs to be called again. | ||
// Some implementations may set this if not all bytes have been read/written. | ||
UNITYTLS_USER_STREAM_CLOSED, // Can be set by the user to cancel a read/write operation. | ||
UNITYTLS_USER_READ_FAILED, // Can be set by the user to indicate a failed read operation. | ||
UNITYTLS_USER_WRITE_FAILED, // Can be set by the user to indicate a failed write operation. | ||
UNITYTLS_USER_UNKNOWN_ERROR, // Can be set by the user to indicate a generic error. | ||
} | ||
|
||
[StructLayout (LayoutKind.Sequential)] | ||
public struct unitytls_errorstate | ||
{ | ||
private UInt32 magic; | ||
public unitytls_error_code code; | ||
private UInt64 reserved; // Implementation specific error code/handle. | ||
} | ||
|
||
// ------------------------------------ | ||
// Private Key | ||
// ------------------------------------ | ||
|
||
public struct unitytls_key {} | ||
[StructLayout (LayoutKind.Sequential)] | ||
public struct unitytls_key_ref { public UInt64 handle; } | ||
|
||
// ------------------------------------ | ||
// X.509 Certificate | ||
// ----------------------------------- | ||
public struct unitytls_x509 {} | ||
[StructLayout (LayoutKind.Sequential)] | ||
public struct unitytls_x509_ref { public UInt64 handle; } | ||
|
||
// ------------------------------------ | ||
// X.509 Certificate List | ||
// ------------------------------------ | ||
public struct unitytls_x509list {} | ||
[StructLayout (LayoutKind.Sequential)] | ||
public struct unitytls_x509list_ref { public UInt64 handle; } | ||
|
||
// ------------------------------------ | ||
// X.509 Certificate Verification | ||
// ------------------------------------ | ||
[Flags] | ||
public enum unitytls_x509verify_result : UInt32 | ||
{ | ||
UNITYTLS_X509VERIFY_SUCCESS = 0x00000000, | ||
UNITYTLS_X509VERIFY_NOT_DONE = 0x80000000, | ||
UNITYTLS_X509VERIFY_FATAL_ERROR = 0xFFFFFFFF, | ||
|
||
UNITYTLS_X509VERIFY_FLAG_EXPIRED = 0x00000001, | ||
UNITYTLS_X509VERIFY_FLAG_REVOKED = 0x00000002, | ||
UNITYTLS_X509VERIFY_FLAG_CN_MISMATCH = 0x00000004, | ||
UNITYTLS_X509VERIFY_FLAG_NOT_TRUSTED = 0x00000008, | ||
|
||
UNITYTLS_X509VERIFY_FLAG_USER_ERROR1 = 0x00010000, | ||
UNITYTLS_X509VERIFY_FLAG_USER_ERROR2 = 0x00020000, | ||
UNITYTLS_X509VERIFY_FLAG_USER_ERROR3 = 0x00040000, | ||
UNITYTLS_X509VERIFY_FLAG_USER_ERROR4 = 0x00080000, | ||
UNITYTLS_X509VERIFY_FLAG_USER_ERROR5 = 0x00100000, | ||
UNITYTLS_X509VERIFY_FLAG_USER_ERROR6 = 0x00200000, | ||
UNITYTLS_X509VERIFY_FLAG_USER_ERROR7 = 0x00400000, | ||
UNITYTLS_X509VERIFY_FLAG_USER_ERROR8 = 0x00800000, | ||
|
||
UNITYTLS_X509VERIFY_FLAG_UNKNOWN_ERROR = 0x08000000, | ||
} | ||
|
||
public delegate unitytls_x509verify_result unitytls_x509verify_callback(void* userData, unitytls_x509_ref cert, unitytls_x509verify_result result, unitytls_errorstate* errorState); | ||
|
||
// ------------------------------------ | ||
// TLS Context | ||
// ------------------------------------ | ||
public struct unitytls_tlsctx {} | ||
[StructLayout (LayoutKind.Sequential)] | ||
public struct unitytls_tlsctx_ref { public UInt64 handle; } | ||
|
||
public enum unitytls_ciphersuite : UInt32 | ||
{ | ||
// With exception of the INVALID value, this enum represents an IANA cipher ID. | ||
UNITYTLS_CIPHERSUITE_INVALID = 0xFFFFFF | ||
} | ||
|
||
public enum unitytls_protocol : UInt32 | ||
{ | ||
UNITYTLS_PROTOCOL_TLS_1_0, | ||
UNITYTLS_PROTOCOL_TLS_1_1, | ||
UNITYTLS_PROTOCOL_TLS_1_2, | ||
|
||
UNITYTLS_PROTOCOL_INVALID, | ||
} | ||
[StructLayout (LayoutKind.Sequential)] | ||
public struct unitytls_tlsctx_protocolrange | ||
{ | ||
public unitytls_protocol min; | ||
public unitytls_protocol max; | ||
}; | ||
|
||
public delegate size_t unitytls_tlsctx_write_callback(void* userData, UInt8* data, size_t bufferLen, unitytls_errorstate* errorState); | ||
public delegate size_t unitytls_tlsctx_read_callback(void* userData, UInt8* buffer, size_t bufferLen, unitytls_errorstate* errorState); | ||
public delegate void unitytls_tlsctx_trace_callback(void* userData, unitytls_tlsctx* ctx, Int8* traceMessage, size_t traceMessageLen); | ||
public delegate unitytls_x509verify_result unitytls_tlsctx_x509verify_callback(void* userData, unitytls_x509list_ref chain, unitytls_errorstate* errorState); | ||
|
||
[StructLayout (LayoutKind.Sequential)] | ||
public struct unitytls_tlsctx_callbacks | ||
{ | ||
public unitytls_tlsctx_read_callback read; | ||
public unitytls_tlsctx_write_callback write; | ||
public void* data; | ||
}; | ||
|
||
|
||
|
||
// ------------------------------------------------------------------------ | ||
// unitytls interface defintion | ||
// ------------------------------------------------------------------------ | ||
|
||
// This native struct is used to provide all necessary fields and function calls from unitytls to the mono-unitytls-binding. | ||
// Native implementation lives in unity:Modules/TLS/TLSMonoIntegration.cpp and needs to be adapted to every change. | ||
[StructLayout (LayoutKind.Sequential)] | ||
public class mono_unity_unitytls_interface | ||
{ | ||
public readonly UInt64 UNITYTLS_INVALID_HANDLE; | ||
|
||
public delegate unitytls_errorstate unitytls_errorstate_create_t(); | ||
public unitytls_errorstate_create_t unitytls_errorstate_create; | ||
public delegate void unitytls_errorstate_raise_error_t(unitytls_errorstate* errorState, unitytls_error_code errorCode); | ||
public unitytls_errorstate_raise_error_t unitytls_errorstate_raise_error; | ||
|
||
public delegate unitytls_key_ref unitytls_key_get_ref_t(unitytls_key* key, unitytls_errorstate* errorState); | ||
public unitytls_key_get_ref_t unitytls_key_get_ref; | ||
public delegate unitytls_key* unitytls_key_parse_der_t(UInt8* buffer, size_t bufferLen, UInt8* password, size_t passwordLen, unitytls_errorstate* errorState); | ||
public unitytls_key_parse_der_t unitytls_key_parse_der; | ||
public delegate void unitytls_key_free_t(unitytls_key* key); | ||
public unitytls_key_free_t unitytls_key_free; | ||
|
||
public delegate size_t unitytls_x509_export_der_t(unitytls_x509_ref cert, UInt8* buffer, size_t bufferLen, unitytls_errorstate* errorState); | ||
public unitytls_x509_export_der_t unitytls_x509_export_der; | ||
|
||
public delegate unitytls_x509list_ref unitytls_x509list_get_ref_t(unitytls_x509list* list, unitytls_errorstate* errorState); | ||
public unitytls_x509list_get_ref_t unitytls_x509list_get_ref; | ||
public delegate unitytls_x509_ref unitytls_x509list_get_x509_t(unitytls_x509list_ref list, size_t index, unitytls_errorstate* errorState); | ||
public unitytls_x509list_get_x509_t unitytls_x509list_get_x509; | ||
public delegate unitytls_x509list* unitytls_x509list_create_t(unitytls_errorstate* errorState); | ||
public unitytls_x509list_create_t unitytls_x509list_create; | ||
public delegate void unitytls_x509list_append_t(unitytls_x509list* list, unitytls_x509_ref cert, unitytls_errorstate* errorState); | ||
public unitytls_x509list_append_t unitytls_x509list_append; | ||
public delegate void unitytls_x509list_append_der_t(unitytls_x509list* list, UInt8* buffer, size_t bufferLen, unitytls_errorstate* errorState); | ||
public unitytls_x509list_append_der_t unitytls_x509list_append_der; | ||
public delegate void unitytls_x509list_free_t(unitytls_x509list* list); | ||
public unitytls_x509list_free_t unitytls_x509list_free; | ||
|
||
public delegate unitytls_x509verify_result unitytls_x509verify_default_ca_t(unitytls_x509list_ref chain, Int8* cn, size_t cnLen, unitytls_x509verify_callback cb, void* userData, unitytls_errorstate* errorState); | ||
public unitytls_x509verify_default_ca_t unitytls_x509verify_default_ca; | ||
public delegate unitytls_x509verify_result unitytls_x509verify_explicit_ca_t(unitytls_x509list_ref chain, unitytls_x509list_ref trustCA, Int8* cn, size_t cnLen, unitytls_x509verify_callback cb, void* userData, unitytls_errorstate* errorState); | ||
public unitytls_x509verify_explicit_ca_t unitytls_x509verify_explicit_ca; | ||
|
||
public delegate unitytls_tlsctx* unitytls_tlsctx_create_server_t(unitytls_tlsctx_protocolrange supportedProtocols, unitytls_tlsctx_callbacks callbacks, unitytls_x509list_ref certChain, unitytls_key_ref leafCertificateKey, unitytls_errorstate* errorState); | ||
public unitytls_tlsctx_create_server_t unitytls_tlsctx_create_server; | ||
public delegate unitytls_tlsctx* unitytls_tlsctx_create_client_t(unitytls_tlsctx_protocolrange supportedProtocols, unitytls_tlsctx_callbacks callbacks, Int8* cn, size_t cnLen, unitytls_errorstate* errorState); | ||
public unitytls_tlsctx_create_client_t unitytls_tlsctx_create_client; | ||
public delegate void unitytls_tlsctx_set_trace_callback_t(unitytls_tlsctx* ctx, unitytls_tlsctx_trace_callback cb, void* userData, unitytls_errorstate* errorState); | ||
public unitytls_tlsctx_set_trace_callback_t unitytls_tlsctx_set_trace_callback; | ||
public delegate void unitytls_tlsctx_set_x509verify_callback_t(unitytls_tlsctx* ctx, unitytls_tlsctx_x509verify_callback cb, void* userData, unitytls_errorstate* errorState); | ||
public unitytls_tlsctx_set_x509verify_callback_t unitytls_tlsctx_set_x509verify_callback; | ||
public delegate void unitytls_tlsctx_set_supported_ciphersuites_t(unitytls_tlsctx* ctx, unitytls_ciphersuite* supportedCiphersuites, size_t supportedCiphersuitesLen, unitytls_errorstate* errorState); | ||
public unitytls_tlsctx_set_supported_ciphersuites_t unitytls_tlsctx_set_supported_ciphersuites; | ||
public delegate unitytls_ciphersuite unitytls_tlsctx_get_ciphersuite_t(unitytls_tlsctx* ctx, unitytls_errorstate* errorState); | ||
public unitytls_tlsctx_get_ciphersuite_t unitytls_tlsctx_get_ciphersuite; | ||
public delegate unitytls_protocol unitytls_tlsctx_get_protocol_t(unitytls_tlsctx* ctx, unitytls_errorstate* errorState); | ||
public unitytls_tlsctx_get_protocol_t unitytls_tlsctx_get_protocol; | ||
public delegate unitytls_x509verify_result unitytls_tlsctx_process_handshake_t(unitytls_tlsctx* ctx, unitytls_errorstate* errorState); | ||
public unitytls_tlsctx_process_handshake_t unitytls_tlsctx_process_handshake; | ||
public delegate size_t unitytls_tlsctx_read_t(unitytls_tlsctx* ctx, UInt8* buffer, size_t bufferLen, unitytls_errorstate* errorState); | ||
public unitytls_tlsctx_read_t unitytls_tlsctx_read; | ||
public delegate size_t unitytls_tlsctx_write_t(unitytls_tlsctx* ctx, UInt8* data, size_t bufferLen, unitytls_errorstate* errorState); | ||
public unitytls_tlsctx_write_t unitytls_tlsctx_write; | ||
public delegate void unitytls_tlsctx_free_t(unitytls_tlsctx* ctx); | ||
public unitytls_tlsctx_free_t unitytls_tlsctx_free; | ||
} | ||
|
||
[DllImport("__Internal")] | ||
private static extern IntPtr mono_unity_get_unitytls_interface(); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This should likely be an internal call and connected via the normal mono icall mechanism for class libraries. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. As is, we need to be sure to export this symbol across all platforms which is not always easy. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Switched it to icall: 65af3e7 |
||
|
||
private static mono_unity_unitytls_interface marshalledInterface = null; | ||
|
||
public static bool IsSupported() | ||
{ | ||
try { | ||
return NativeInterface != null; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It seems wasteful to try/catch in here, although I guess this is only called once. Any reason this can't be a little cleaner? Perhaps There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. My idea here was that I would also catch any exception thrown from There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. (done) |
||
} catch (System.Exception) { | ||
return false; | ||
} | ||
} | ||
|
||
public static mono_unity_unitytls_interface NativeInterface | ||
{ | ||
get | ||
{ | ||
if (marshalledInterface == null) | ||
marshalledInterface = Marshal.PtrToStructure<mono_unity_unitytls_interface>(mono_unity_get_unitytls_interface()); | ||
return marshalledInterface; | ||
} | ||
} | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You like want
SByte
here.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I intentionally put
Byte
here since what we get on the C# side from encoding and marshaling functions are alwaysbyte[]
Unitytls uses UInt8 to denote raw buffers and Int8/char for strings. Since we need to push all in and outgoing strings through
Encoding
it is easier to pass byte* along. The aliases here are just there to keep the semantic in the interface and make it more similar to the c original.I guess I should add a comment explaining that!