Have you ever wanted (or needed!) to encrypt a C# object’s children with potentially different keys or different methods? How, then, do you implement inline object encryption in a way that’s essentially transparent to a developer?
I’ve been wondering about this issue for 5 years and finally decided to prototype it. This concept has come to fruition in several different ways, none of which are particularly simple to read or maintain, which has resulted in a rewrite of the idea about 15 different times.
I believe that this implementation is the cleanest and extensible way to create a managed-access object; utilizing dynamic proxies to encapsulate properties within a target object.
Here’s my solution to the first half of this issue: how do you utilize C# to access a different part of memory than originally intended?
The Basis
In C#, an auto-property is made up of four items; the property itself, two methods, and a backing field. In order to “override” this property, we must override the two methods and point them at an alternative backing field. This gives us the freedom as well to be able to cast back and forth between the property’s type and some other type (which would be the type of the new backing field).
In order to do this, first open up Microsoft Visual Studio and grab a copy of Castle.Core library from NuGet, to get the DynamicProxy functionality. This is Apache 2.0 licensed, so it works in most projects, commercial and otherwise (as of the time of this writing, I believe that to be true; however, I am not a lawyer and this does not constitute legal advice)
The most basic use of Castle.Core amounts to this:
var options = new ProxyGenerationOptions(); options.AddMixinInstance(someInstanceOfClass); return Generator.CreateClassProxy<T>(options, new UserInterceptor());
First we create some instance of a class that inherits from an interface. The interface defines what methods/properties will end up being accessible once the proxy is generated.
We then add this instance as a “Mixin“, Castle’s term for a class that will be used to extend the class being proxied (in the example above, that is “T”).
Finally, we create the proxy and tell it to use the interceptor called “UserInterceptor”. An interceptor is a class that contains a method that is invoked when anything is called within the proxied type (and by extension, its mixins). This allows the developer to retarget the call, or suppress it, or whatever else is necessary.
The Magic
Generating a Dynamic Proxy Mixin
Since we are wrapping any class with properties for this example, we need to write code that utilizes DynamicProxy’s Mixin functionality. This Mixin will be generated using dynamic assembly injection so we can write a class that best underpins whatever we are trying to wrap.
Our goal with this is to create additional backing fields and property members, which we will replace the original property’s method pointers with. In this way, we end up diverting execution away from the auto-property’s get and set methods to the ones we choose.
One of the key “gotchas” to remember with Castle DynamicProxy is that the Mixin must inherit from an interface. This interface then represents the additional items to inject into the generated proxy. So, let’s start by generating the interface:
private static TypeBuilder CreateInterfaceType(string name, PropertyInfo[] properties) { TypeBuilder typeBuilder = moduleBuilder.DefineType(name, TypeAttributes.Public | TypeAttributes.Abstract | TypeAttributes.AutoClass | TypeAttributes.AnsiClass | TypeAttributes.Interface); foreach (var property in properties) { var newProperty = typeBuilder.DefineProperty(BACKING_PROPERTY_PREFIX + property.Name, PropertyAttributes.None, typeof(byte[]), null); var newPropertyGet = typeBuilder.DefineMethod( "get_" + newProperty.Name, MethodAttributes.Abstract | MethodAttributes.Virtual | MethodAttributes.Public | MethodAttributes.SpecialName, typeof(byte[]), Type.EmptyTypes); var newPropertySet = typeBuilder.DefineMethod( "set_" + newProperty.Name, MethodAttributes.Abstract | MethodAttributes.Virtual | MethodAttributes.Public | MethodAttributes.SpecialName, null, new Type[] { typeof(byte[]) }); newProperty.SetGetMethod(newPropertyGet); newProperty.SetSetMethod(newPropertySet); } typeBuilder.CreateType(); return typeBuilder; }
Here, we pass this CreateInterfaceType method the name of the interface that the proxy extension will implement, and a list of PropertyInfo objects from the type to be wrapped. This will then return an interface type that implements virtual getters and setters for each property being wrapped.
While the interface generation is fairly straightforward, the implemented class generation is a bit more tricky:
private static TypeBuilder CreateImplementationType(string name, PropertyInfo[] properties) { TypeBuilder typeBuilder = moduleBuilder.DefineType(name, TypeAttributes.Public | TypeAttributes.Class | TypeAttributes.AutoClass | TypeAttributes.AnsiClass | TypeAttributes.AutoLayout, typeof(object)); foreach (PropertyInfo pi in properties) { string piName = pi.Name; Type propertyType = pi.PropertyType; FieldBuilder field = typeBuilder.DefineField("_" + piName, propertyType, FieldAttributes.Private); MethodInfo getMethod = pi.GetGetMethod(); if (getMethod != null) { MethodBuilder methodBuilder = typeBuilder.DefineMethod( getMethod.Name, MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.Virtual, propertyType, Type.EmptyTypes); var ilGenerator = methodBuilder.GetILGenerator(); ilGenerator.Emit(OpCodes.Ldarg_0); // Load "this" ilGenerator.Emit(OpCodes.Ldfld, field); // Load the backing field ilGenerator.Emit(OpCodes.Ret); // Return the value on the stack (field) // Override the get method from the original type to this IL-generated version typeBuilder.DefineMethodOverride(methodBuilder, getMethod); } var setMethod = pi.GetSetMethod(); if (setMethod != null) { var methodBuilder = typeBuilder.DefineMethod( setMethod.Name, MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.Virtual, typeof (void), new Type[] {pi.PropertyType}); var ilGenerator = methodBuilder.GetILGenerator(); ilGenerator.Emit(OpCodes.Ldarg_0); // Load "this" ilGenerator.Emit(OpCodes.Ldarg_1); // Load the set value ilGenerator.Emit(OpCodes.Stfld, field); // Set the field to the set value ilGenerator.Emit(OpCodes.Ret); // Override the set method from the original type to this IL-generated version typeBuilder.DefineMethodOverride(methodBuilder, setMethod); } } return typeBuilder; }
This method generates an instance of a new type which looks very similar to our interface, except it uses emitted IL to write the get and set methods for the properties. These are written like autoproperties (they just set and retrieve the value from the backing field).
One thing to note here is when calling the method, PropertyInfo[] should be the list of properties from the interface type we just created.
Once this type is generated, the last steps are to link these two together and create the resulting type, which is achieved simply with the code below:
// These represent calls to the methods we've written above. var wrappedType = new UserExternalObjectToProxy().GetType(); var properties = wrappedType.GetProperties().Where( p => p.GetGetMethod().IsVirtual && p.GetSetMethod().IsVirtual).ToArray(); var interfaceType = CreateInterfaceType(INTERFACE_TYPE_PREFIX + wrappedType.Name, properties); var implementedType = CreateImplementationType(IMPLEMENTED_TYPE_PREFIX + wrappedType.Name, interfaceType.GetProperties(), wrappedType); implementedType.AddInterfaceImplementation(interfaceType); var createdImplementedType = implementedType.CreateType(); var proxyMixinInstance = Activator.CreateInstance(createdImplementedType);
The final variable seen above, proxyMixinInstance, can be used with AddMixinInstance (from the minimal base example at the top) to add the mixin to the proxy being generated.
Writing a Custom Interceptor
The interceptor is the easiest part of all of this. One crucial thing I left out of the section on writing a Mixin is to register your properties with the interceptor in some way. This could be as easy as having a set of properties and types to track, stored statically in the interceptor (this is how the example code is written).
Before we see the code, I’ll explain how it is supposed to work:
- The invocation is intercepted by the Intercept(IInvocation) method.
- The interceptor checks if the method being called starts with “get_” or “set_” (.NET’s internal way of accessing/mutating properties)
- The get_/set_ prefix is removed to get the name of the native property
- The list of properties is checked to see if the property being called upon is tracked by our interceptor
- If so, we retrieve the backing property from the proxy’s Mixin and execute GetValue or SetValue
So, all that said, let’s look at how it operates:
internal static List<Tuple<Type,string>> InterceptedProperties { get; private set; } internal static Func<object, byte[]> SerializationFunction { get; set; } internal static Func<byte[], object> DeserializationFunction { get; set; } public void Intercept(IInvocation invocation) { if (invocation.Method.Name.StartsWith("set_") || invocation.Method.Name.StartsWith("get_")) { var propertyName = invocation.Method.Name.Substring(4); // remove get_ or set_ var targetInterception = InterceptedProperties.FirstOrDefault( p => p.Item1 == invocation.TargetType && p.Item2 == propertyName); if (targetInterception != null) { var property = invocation.Proxy.GetType().GetProperty(StorageExtensionFactory.BACKING_PROPERTY_PREFIX + propertyName); if (invocation.Method.Name.Replace(propertyName, "") == "get_") { var proxyValue = (byte[]) property.GetValue(invocation.Proxy, null); if (proxyValue != null) { var value = DeserializationFunction.Invoke(proxyValue); SetInvocationReturn(invocation, value); } else SetInvocationReturn(invocation, null); return; } else if (invocation.Method.Name.Replace(propertyName, "") == "set_") { property.SetValue(invocation.Proxy, SerializationFunction.Invoke(invocation.Arguments[0]), null); return; } } } invocation.Proceed(); } private static void SetInvocationReturn(IInvocation invocation, object value) { if (value == null) { if (invocation.Method.ReturnType.IsValueType) invocation.ReturnValue = Activator.CreateInstance(invocation.Method.ReturnType); else invocation.ReturnValue = null; } else { invocation.ReturnValue = value; } }
You’ll notice my use of the function SetInvocationReturn. This method accounts for the fact that some of our types are nullable and some are not. This way, if we see a value type, we use Activator to create a default value programmatically (default(T) is not programmatic; this is the next best way in my opinion) — otherwise, we assign null.
I will continue this exploration of SerializationFunction and DeserializationFunction in my next post, with the same topic in the context of a larger project. My next post will show to what I’m pinning these functions and add onto this interesting (ab)use of C#’s properties.
My follow-up to this post will be available soon. It covers the complementary question: how do you implement multi-method encryption dynamically?
Please follow my blog via email (using the button on the sidebar) or on WordPress.com to get the second part of this post as soon as it’s available!