1 // CommonCrypto bindings for MonoMac and MonoTouch
2 //
3 // Authors:
4 //	Sebastien Pouliot  <sebastien@xamarin.com>
5 //
6 // Copyright 2012-2014 Xamarin Inc.
7 
8 using System;
9 using System.Security.Cryptography;
10 using System.Runtime.InteropServices;
11 
12 namespace Crimson.CommonCrypto {
13 
14 	// int32_t -> CommonCryptor.h
15 	enum CCCryptorStatus {
16 	    Success			= 0,
17     	ParamError		= -4300,
18 	    BufferTooSmall	= -4301,
19     	MemoryFailure	= -4302,
20     	AlignmentError	= -4303,
21     	DecodeError		= -4304,
22     	Unimplemented	= -4305
23 	}
24 
25 	// uint32_t -> CommonCryptor.h
26 	// note: not exposed publicly so it can stay signed
27 	enum CCOperation {
28 		Encrypt = 0,
29 		Decrypt,
30 	}
31 
32 	// uint32_t -> CommonCryptor.h
33 	// note: not exposed publicly so it can stay signed
34 	enum CCAlgorithm {
35 		AES128 = 0,
36 		DES,
37 		TripleDES,
38 		CAST,
39 		RC4,
40 		RC2,
41 		Blowfish
42 	}
43 
44 	// uint32_t -> CommonCryptor.h
45 	// note: not exposed publicly so it can stay signed
46 	[Flags]
47 	enum CCOptions {
48 		None			= 0,
49 		PKCS7Padding	= 1,
50 		ECBMode			= 2
51 	}
52 
53 	static class Cryptor {
54 
55 		const string libSystem = "/usr/lib/libSystem.dylib";
56 
57 		// size_t was changed to IntPtr for 32/64 bits size difference - even if mono is (moslty) used in 32bits only on OSX today
58 		// not using `nint` to be able to resue this outside (if needed)
59 
60 		[DllImport (libSystem)]
CCCryptorCreate(CCOperation op, CCAlgorithm alg, CCOptions options, byte[] key, IntPtr keyLength, byte[] iv, ref IntPtr cryptorRef)61 		extern internal static CCCryptorStatus CCCryptorCreate (CCOperation op, CCAlgorithm alg, CCOptions options, /* const void* */ byte[] key, /* size_t */ IntPtr keyLength, /* const void* */ byte[] iv, /* CCCryptorRef* */ ref IntPtr cryptorRef);
62 
63 		[DllImport (libSystem)]
CCCryptorRelease( IntPtr cryptorRef)64 		extern internal static CCCryptorStatus CCCryptorRelease (/* CCCryptorRef */ IntPtr cryptorRef);
65 
66 		[DllImport (libSystem)]
CCCryptorUpdate( IntPtr cryptorRef, byte[] dataIn, IntPtr dataInLength, byte[] dataOut, IntPtr dataOutAvailable, ref IntPtr dataOutMoved)67 		extern internal static CCCryptorStatus CCCryptorUpdate (/* CCCryptorRef */ IntPtr cryptorRef, /* const void* */ byte[] dataIn, /* size_t */ IntPtr dataInLength, /* void* */ byte[] dataOut, /* size_t */ IntPtr dataOutAvailable, /* size_t* */ ref IntPtr dataOutMoved);
68 
69 		[DllImport (libSystem)]
CCCryptorUpdate( IntPtr cryptorRef, IntPtr dataIn, IntPtr dataInLength, IntPtr dataOut, IntPtr dataOutAvailable, ref IntPtr dataOutMoved)70 		extern internal static CCCryptorStatus CCCryptorUpdate (/* CCCryptorRef */ IntPtr cryptorRef, /* const void* */ IntPtr dataIn, /* size_t */ IntPtr dataInLength, /* void* */ IntPtr dataOut, /* size_t */ IntPtr dataOutAvailable, /* size_t* */ ref IntPtr dataOutMoved);
71 
72 		[DllImport (libSystem)]
CCCryptorFinal( IntPtr cryptorRef, byte[] dataOut, IntPtr dataOutAvailable, ref IntPtr dataOutMoved)73 		extern internal static CCCryptorStatus CCCryptorFinal (/* CCCryptorRef */ IntPtr cryptorRef, /* void* */ byte[] dataOut, /* size_t */ IntPtr dataOutAvailable, /* size_t* */ ref IntPtr dataOutMoved);
74 
75 		[DllImport (libSystem)]
CCCryptorGetOutputLength( IntPtr cryptorRef, IntPtr inputLength, bool final)76 		extern internal static int CCCryptorGetOutputLength (/* CCCryptorRef */ IntPtr cryptorRef, /* size_t */ IntPtr inputLength, bool final);
77 
78 		[DllImport (libSystem)]
CCCryptorReset( IntPtr cryptorRef, IntPtr iv)79 		extern internal static CCCryptorStatus CCCryptorReset (/* CCCryptorRef */ IntPtr cryptorRef, /* const void* */ IntPtr iv);
80 
81 		// helper method to reduce the amount of generate code for each cipher algorithm
Create(CCOperation operation, CCAlgorithm algorithm, CCOptions options, byte[] key, byte[] iv)82 		static internal IntPtr Create (CCOperation operation, CCAlgorithm algorithm, CCOptions options, byte[] key, byte[] iv)
83 		{
84 			if (key == null)
85 				throw new CryptographicException ("A null key was provided");
86 
87 			// unlike the .NET framework CommonCrypto does not support two-keys triple-des (128 bits) ref: #6967
88 			if ((algorithm == CCAlgorithm.TripleDES) && (key.Length == 16)) {
89 				byte[] key3 = new byte [24];
90 				Buffer.BlockCopy (key, 0, key3, 0, 16);
91 				Buffer.BlockCopy (key, 0, key3, 16, 8);
92 				key = key3;
93 			}
94 
95 			IntPtr cryptor = IntPtr.Zero;
96 			CCCryptorStatus status = Cryptor.CCCryptorCreate (operation, algorithm, options, key, (IntPtr) key.Length, iv, ref cryptor);
97 			if (status != CCCryptorStatus.Success)
98 				throw new CryptographicUnexpectedOperationException ();
99 			return cryptor;
100 		}
101 
102 		// size_t was changed to IntPtr for 32/64 bits size difference - even if mono is (moslty) used in 32bits only on OSX today
103 		[DllImport ("/System/Library/Frameworks/Security.framework/Security")]
SecRandomCopyBytes( IntPtr rnd, IntPtr count, byte[] bytes)104 		extern internal static /* int */ int SecRandomCopyBytes (/* SecRandomRef */ IntPtr rnd, /* size_t */ IntPtr count, /* uint8_t* */ byte[] bytes);
105 
GetRandom(byte[] buffer)106 		static internal void GetRandom (byte[] buffer)
107 		{
108 			if (SecRandomCopyBytes (IntPtr.Zero, (IntPtr) buffer.Length, buffer) != 0)
109 				throw new CryptographicException (Marshal.GetLastWin32Error ()); // errno
110 		}
111 	}
112 
113 #if !MONOTOUCH && !XAMMAC
114 	static class KeyBuilder {
Key(int size)115 		static public byte[] Key (int size)
116 		{
117 			byte[] buffer = new byte [size];
118 			Cryptor.GetRandom (buffer);
119 			return buffer;
120 		}
121 
IV(int size)122 		static public byte[] IV (int size)
123 		{
124 			byte[] buffer = new byte [size];
125 			Cryptor.GetRandom (buffer);
126 			return buffer;
127 		}
128 	}
129 #endif
130 }
131