Skip to content
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

Version 0.2.0: All public components now use Properties instead of fields #27

Merged
merged 12 commits into from
Mar 27, 2020
81 changes: 44 additions & 37 deletions BO4E-dotnet.Encryption/Anonymizer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,18 @@
using System.Security.Cryptography;
using System.Security.Cryptography.X509Certificates;
using System.Text;

using BO4E.BO;
using BO4E.Extensions.BusinessObjects;
using BO4E.meta;
using BO4E.meta.LenientConverters;

using Microsoft.CSharp.RuntimeBinder;
using Microsoft.Extensions.Logging;

using Newtonsoft.Json;
using Newtonsoft.Json.Linq;

using Org.BouncyCastle.Crypto;

namespace BO4E.Extensions.Encryption
Expand Down Expand Up @@ -117,10 +121,13 @@ public T ApplyOperations<T>(BusinessObject bo)
foreach (DataCategory dataCategory in mapping.Keys)
{
AnonymizerApproach approach = mapping[dataCategory];
FieldInfo[] affectedFields = BoMapper.GetAnnotatedFields(bo.GetType());
foreach (FieldInfo affectedField in affectedFields)
PropertyInfo[] affectedProps = bo.GetType().GetProperties()
.Where(p => p.GetCustomAttributes(typeof(DataCategoryAttribute), false).Length > 0)
.OrderBy(ap => ap.GetCustomAttribute<JsonPropertyAttribute>()?.Order)
.ToArray<PropertyInfo>();
foreach (PropertyInfo affectedProp in affectedProps)
{
if (!affectedField.IsAnonymizerRelevant(approach, dataCategory))
if (!affectedProp.IsAnonymizerRelevant(approach, dataCategory))
{
continue;
}
Expand All @@ -129,9 +136,9 @@ public T ApplyOperations<T>(BusinessObject bo)
case AnonymizerApproach.HASH:
try
{
object affectedFieldValue = affectedField.GetValue(result);
object affectedFieldValue = affectedProp.GetValue(result);
HashObject(ref affectedFieldValue, dataCategory);
affectedField.SetValue(result, affectedFieldValue);
affectedProp.SetValue(result, affectedFieldValue);
}
catch (RuntimeBinderException e)
{
Expand All @@ -145,26 +152,26 @@ public T ApplyOperations<T>(BusinessObject bo)
* annotated default value, otherwise we can safely set null. */
bool isRequired = false;
Attribute defaultValueAttribute = null;
Attribute jsonPropertyAttribute = affectedField.GetCustomAttributes().Where(a => a.GetType() == typeof(JsonPropertyAttribute)).FirstOrDefault();
Attribute jsonPropertyAttribute = affectedProp.GetCustomAttributes().Where(a => a.GetType() == typeof(JsonPropertyAttribute)).FirstOrDefault();
if (jsonPropertyAttribute != null)
{
JsonPropertyAttribute jpa = (JsonPropertyAttribute)jsonPropertyAttribute;
if (jpa.Required == Required.Always)
{
isRequired = true;
defaultValueAttribute = affectedField.GetCustomAttributes().Where(a => a.GetType() == typeof(DefaultValueAttribute)).FirstOrDefault();
defaultValueAttribute = affectedProp.GetCustomAttributes().Where(a => a.GetType() == typeof(DefaultValueAttribute)).FirstOrDefault();
}
}

if (isRequired && defaultValueAttribute != null)
{
DefaultValueAttribute dva = (DefaultValueAttribute)defaultValueAttribute;
affectedField.SetValue(result, dva.Value);
affectedProp.SetValue(result, dva.Value);
}
else if (bo.GetType().IsSubclassOf(typeof(BO4E.BO.BusinessObject)))
{
// e.g. 1*Energiemenge (mappedObject)---> n*Verbrauch (boSubObject)
var boSubObject = affectedField.GetValue(bo);
var boSubObject = affectedProp.GetValue(bo);
if (boSubObject != null)
{
try
Expand All @@ -185,13 +192,13 @@ public T ApplyOperations<T>(BusinessObject bo)
{
_logger.LogError($"Couldn't null BO field!: {e.Message}");
}
affectedField.SetValue(result, boSubObject);
affectedProp.SetValue(result, boSubObject);
}
}
else
{
// strings, integers, elementary
affectedField.SetValue(result, null);
affectedProp.SetValue(result, null);
}
break;
case AnonymizerApproach.ENCRYPT:
Expand All @@ -201,17 +208,17 @@ public T ApplyOperations<T>(BusinessObject bo)
}
using (X509AsymmetricEncrypter xasyncenc = new X509AsymmetricEncrypter(this.publicKeyX509))
{
if (affectedField.GetValue(bo).GetType() == typeof(string))
if (affectedProp.GetValue(bo).GetType() == typeof(string))
{
if (affectedField.GetValue(bo) != null)
if (affectedProp.GetValue(bo) != null)
{
affectedField.SetValue(result, xasyncenc.Encrypt(affectedField.GetValue(bo).ToString()));
affectedProp.SetValue(result, xasyncenc.Encrypt(affectedProp.GetValue(bo).ToString()));
}
}
else if (affectedField.GetValue(bo).GetType().IsSubclassOf(typeof(BO4E.COM.COM)))
else if (affectedProp.GetValue(bo).GetType().IsSubclassOf(typeof(BO4E.COM.COM)))
{
var comObject = affectedField.GetValue(bo);
dynamic comFields = comObject.GetType().GetFields();
var comObject = affectedProp.GetValue(bo);
dynamic comFields = comObject.GetType().GetProperties();
foreach (dynamic comField in comFields)
{
try
Expand All @@ -229,21 +236,21 @@ public T ApplyOperations<T>(BusinessObject bo)
_logger.LogError($"Couldn't encrypt COM field!: {f.Message}");
}
}
affectedField.SetValue(result, comObject);
affectedProp.SetValue(result, comObject);
}
else if (affectedField.FieldType.IsSubclassOf(typeof(BO4E.BO.BusinessObject)))
else if (affectedProp.PropertyType.IsSubclassOf(typeof(BO4E.BO.BusinessObject)))
{
affectedField.SetValue(result, xasyncenc.Encrypt((BusinessObject)affectedField.GetValue(bo)));
affectedProp.SetValue(result, xasyncenc.Encrypt((BusinessObject)affectedProp.GetValue(bo)));
}
else if (affectedField.FieldType.ToString().StartsWith("BO4E.ENUM")) // todo: check for namespace instead of strinyfied comparison
else if (affectedProp.PropertyType.ToString().StartsWith("BO4E.ENUM")) // todo: check for namespace instead of strinyfied comparison
{
//affectedField.SetValue(mappedObject, Sha256HashEnum(affectedField.GetValue(mappedObject).ToString()));
_logger.LogWarning($"Encrypting {affectedField.FieldType} is not supported, since the result would not be a valid ENUM value.");
_logger.LogWarning($"Encrypting {affectedProp.PropertyType} is not supported, since the result would not be a valid ENUM value.");
//throw new NotSupportedException($"Hashing {affectedField.FieldType} is not supported, since the result would not be a valid ENUM value.");
}
else
{
throw new NotImplementedException($"Encrypting {affectedField.FieldType} is not implemented yet.");
throw new NotImplementedException($"Encrypting {affectedProp.PropertyType} is not implemented yet.");
}
}
break;
Expand All @@ -254,7 +261,7 @@ public T ApplyOperations<T>(BusinessObject bo)
}
using (X509AsymmetricEncrypter xasydec = new X509AsymmetricEncrypter(this.privateKey))
{
affectedField.SetValue(result, xasydec.Decrypt(affectedField.GetValue(bo).ToString()));
affectedProp.SetValue(result, xasydec.Decrypt(affectedProp.GetValue(bo).ToString()));
}
continue;
case AnonymizerApproach.KEEP:
Expand Down Expand Up @@ -337,25 +344,25 @@ protected void HashObject(ref object input, DataCategory? dataCategory = null)
}
else
{
var fields = inputType.GetFields();
foreach (var field in fields)
var properties = inputType.GetProperties();
foreach (var prop in properties)
{
if (field.GetValue(input) == null || !field.IsHashingRelevant(dataCategory))
if (prop.GetValue(input) == null || !prop.IsHashingRelevant(dataCategory))
{
continue;
}
try
{
object o = field.GetValue(input);
object o = prop.GetValue(input);
HashObject(ref o, dataCategory);
field.SetValue(input, o);
prop.SetValue(input, o);
}
catch (Exception f)
{
throw new ArgumentException($"Couldn't hash field {field.Name}: {f.Message}");
throw new ArgumentException($"Couldn't hash field {prop.Name}: {f.Message}");
}
}
if (fields.Count() == 0)
if (properties.Count() == 0)
{
throw new NotImplementedException($"Type {inputType} with value '{input}' has no subfields but is not handled separately.");
}
Expand Down Expand Up @@ -416,28 +423,28 @@ protected void HashString(ref string input, DataCategory? dataCategory)
/// <returns>true if the <see cref="Marktlokation.marktlokationsId"/> fulfills the requirements of a hashed key</returns>
public static bool HasHashedKey(Marktlokation ma)
{
return !string.IsNullOrWhiteSpace(ma.marktlokationsId) && ma.marktlokationsId.StartsWith(HASHED_MARKTLOKATION_PREFIX);
return !string.IsNullOrWhiteSpace(ma.MarktlokationsId) && ma.MarktlokationsId.StartsWith(HASHED_MARKTLOKATION_PREFIX);
}
/// <summary>
/// check if a Messlokation has been pseudonymized using <see cref="AnonymizerApproach.HASH"/>
/// As of 2019 it's impossible for a "real" Messlokation to fulfill this condition.
/// </summary>
/// <param name="me">Messlokation</param>
/// <returns>true if the <see cref="Messlokation.messlokationsId"/> fulfills the requirements of a hashed key</returns>
/// <returns>true if the <see cref="Messlokation.MesslokationsId"/> fulfills the requirements of a hashed key</returns>
public static bool HasHashedKey(Messlokation me)
{
return !string.IsNullOrWhiteSpace(me.messlokationsId) && me.messlokationsId.StartsWith(HASHED_MESSLOKATION_PREFIX);
return !string.IsNullOrWhiteSpace(me.MesslokationsId) && me.MesslokationsId.StartsWith(HASHED_MESSLOKATION_PREFIX);
}

/// <summary>
/// check if an Energiemenge been pseudonymized using <see cref="AnonymizerApproach.HASH"/>.
/// Calls <see cref="IsHashedKey(string)"/> for <see cref="Energiemenge.lokationsId"/>.
/// Calls <see cref="IsHashedKey(string)"/> for <see cref="Energiemenge.LokationsId"/>.
/// </summary>
/// <param name="em">Energiemenge</param>
/// <returns>true if the <see cref="Energiemenge.lokationsId"/> fulfills the requirements of a hashed key</returns>
/// <returns>true if the <see cref="Energiemenge.LokationsId"/> fulfills the requirements of a hashed key</returns>
public static bool HasHashedKey(Energiemenge em)
{
return IsHashedKey(em.lokationsId);
return IsHashedKey(em.LokationsId);
}

/// <summary>
Expand Down
2 changes: 1 addition & 1 deletion BO4E-dotnet.Encryption/AnonymizerConfiguration.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ public class AnonymizerConfiguration
public Dictionary<DataCategory, AnonymizerApproach> operations { get; private set; }

/// <summary>
/// set of key in <see cref="BO4E.BO.BusinessObject.userProperties"/> / <see cref="BO4E.COM.COM.userProperties"/> that should not be affected by the anonymizing operations
/// set of key in <see cref="BO4E.BO.BusinessObject.UserProperties"/> / <see cref="BO4E.COM.COM.UserProperties"/> that should not be affected by the anonymizing operations
/// </summary>
[JsonProperty(Required = Required.Default)]
public HashSet<string> unaffectedUserProperties;
Expand Down
2 changes: 1 addition & 1 deletion BO4E-dotnet.Encryption/AsymmetricEncrypter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ public override BusinessObject Decrypt(EncryptedObject encryptedObject)
{
return null;
}
string plainString = Decrypt(eo.cipherText, eo.publicKey, eo.nonce);
string plainString = Decrypt(eo.CipherText, eo.PublicKey, eo.Nonce);
return JsonConvert.DeserializeObject<BusinessObject>(plainString);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
<PackageId>Hochfrequenz.BO4E.Extensions.Encryption</PackageId>
<Authors />
<Company>Hochfrequenz Unternehmensberatung GmbH</Company>
<Version>0.2.0</Version>
</PropertyGroup>

<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
Expand Down
12 changes: 6 additions & 6 deletions BO4E-dotnet.Encryption/EncryptedObject.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,16 +17,16 @@ public abstract class EncryptedObject : BusinessObject
/// <summary>
/// encryption scheme used
/// </summary>
[JsonProperty(Required = Required.Always, Order = 7)]
[JsonProperty(PropertyName = "encryptionScheme", Required = Required.Always, Order = 7)]
[BoKey]
public EncryptionScheme encryptionScheme;
public EncryptionScheme EncryptionScheme { get; set; }

/// <summary>
/// base64 encoded cipher text of the original objects JSON serialisation
/// </summary>
[JsonProperty(Required = Required.Always, Order = 8)]
[JsonProperty(PropertyName = "cipherText", Required = Required.Always, Order = 8)]
[BoKey]
public string cipherText;
public string CipherText { get; set; }

/// <summary>
/// create a new EncryptedObject instance by providing both
Expand All @@ -35,8 +35,8 @@ public abstract class EncryptedObject : BusinessObject
/// <param name="es">the encryption scheme</param>
public EncryptedObject(string _cipherText, EncryptionScheme es) : base()
{
this.cipherText = _cipherText;
this.encryptionScheme = es;
this.CipherText = _cipherText;
this.EncryptionScheme = es;
}
}
}
13 changes: 7 additions & 6 deletions BO4E-dotnet.Encryption/EncryptedObjectAEAD.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using BO4E.ENUM;

using Newtonsoft.Json;

namespace BO4E.BO
Expand All @@ -13,20 +14,20 @@ public class EncryptedObjectAEAD : EncryptedObject
/// <param name="nonce">unique nonce / initialisation vector (base 64 encoded, must not be used twice)</param>
public EncryptedObjectAEAD(string cipherText, string associatedData, string nonce) : base(cipherText, EncryptionScheme.SodiumSymmetricAEAD)
{
this.associatedData = associatedData;
this.nonce = nonce;
this.AssociatedData = associatedData;
this.Nonce = nonce;
}

/// <summary>
/// base64 encoded unique nonce / initialisation vector
/// </summary>
[JsonProperty(Required = Required.Always, Order = 8)]
public string nonce;
[JsonProperty(PropertyName = "nonce", Required = Required.Always, Order = 8)]
public string Nonce { get; set; }

/// <summary>
/// associated data string (UTF-8); might be an empty string but not null
/// </summary>
[JsonProperty(Required = Required.Always, Order = 5)]
public string associatedData;
[JsonProperty(PropertyName = "AssociatedData", Required = Required.Always, Order = 5)]
public string AssociatedData { get; set; }
}
}
8 changes: 5 additions & 3 deletions BO4E-dotnet.Encryption/EncryptedObjectPKCS7.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
using System.Collections.Generic;

using BO4E.ENUM;

using Newtonsoft.Json;


Expand All @@ -14,13 +16,13 @@ public class EncryptedObjectPKCS7 : EncryptedObject
/// <param name="publicKeys">list of public keys for which the object is decrypt-able </param>
public EncryptedObjectPKCS7(string cipherText, List<string> publicKeys) : base(cipherText, EncryptionScheme.BouncyCastleCMS)
{
this.publicKeys = publicKeys;
this.PublicKeys = publicKeys;
}

/// <summary>
/// list of public keys for which the object is decrypt-able
/// </summary>
[JsonProperty(Required = Required.Default)]
public List<string> publicKeys;
[JsonProperty(PropertyName = "publicKeys", Required = Required.Default)]
public List<string> PublicKeys { get; set; }
}
}
Loading