Skip to content

Commit

Permalink
Version 0.2.0: All public components now use Properties instead of fi…
Browse files Browse the repository at this point in the history
…elds (#27)

* business objects: fields->properties

* COMs: fields->properties

* fix build errors and some IDE suggestions

* ignore naming convention

* update tests in main pacakge

* everything except 1 test...

* fix stupid test

* unittests ftw.

* how did this ever work

* remove deprecated 'messLokationsId'; it's 'messlokationsId' only.

* optimize bo4e code

* Apply fields->properties to Encryption package, too
  • Loading branch information
hf-kklein authored Mar 27, 2020
1 parent 4780de7 commit 7a54650
Show file tree
Hide file tree
Showing 149 changed files with 3,363 additions and 3,171 deletions.
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

0 comments on commit 7a54650

Please sign in to comment.