Skip to content

Commit

Permalink
Latest Develop Branch that Includes Detailed Examples for Multiple Ex…
Browse files Browse the repository at this point in the history
…tensions (#28)

* remove files

* Revert "remove intermediate doc trigger (as docs are actually built here)"

This reverts commit efb8e14.

* remove build process irtself

* remove unused files

* trigger docs only on master commit

* add JSON.net schemas to repository

* add link to official json.net schema docs

* catch license exception

* check if userProperties are null

* Auto stash before merge of "develop" and "origin/develop" (#26)

* Auto stash before merge of "develop" and "origin/develop"

* unify namespaces & put different converters in different files

* must not reuse the current reader here. still not fully understand.

* fix smaller bugs, deactivate outdated tests

* fix null value handling once more.

Co-authored-by: Konstantin Klein <konstantin.klein@hochfrequenz.de>

* fix outdated exception testing

* move stuff out of obsolete class

* modularize more  stuff originally from deprecated BoMapper class

* Version 0.2.0: All public components now use Properties instead of fields (#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

* add all json schema and protobuf files as content files to solution

* fix wrong pathes

* generate all the puml files

puml-gen BO4E-dotnet puml-files -dir -public -excludePaths bin,obj,json-schema-files,Properties,protobuf-files -createAssociation -allInOne
readme.md

* move json-schema files out of solution again

other than the puml-files they are not useful as a content file
.csproj also

* fix broken link

* Explain usage of proto files

* remove fixed version in csproj, otherwise it will not generate suffix

* but should have bumped the prefix in the same commit

* Anrede should be upper case in GP

* Zwischenstand GRPC (was ein Mist ;))

* add showcase tests for github

* Update README.md

* add coverlet nuget to all test projects

* Update README.md

* add a lot of show case tests

* Add showcase for anonymizign

* fix broken links

* Mannheim -> Grünwald


a

Co-authored-by: Joscha Metze <joscha.metze@hochfrequenz.de>
Co-authored-by: Hamid Hajiparvaneh <52197378+hamidd30@users.noreply.github.com>
  • Loading branch information
3 people authored Apr 13, 2020
1 parent e11e393 commit e5123a4
Show file tree
Hide file tree
Showing 407 changed files with 28,161 additions and 4,071 deletions.
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ BO4E-dotnet.Reporting/obj
BO4E-dotnet/obj/

BO4ETestProject/bin/
BO4ETestProject/TestResults/
BO4E-dotnet/bin/

*.manifest
Expand All @@ -35,5 +36,8 @@ TestBO4E-dotnet-Reporting/obj/


TestBO4E-dotnet-Encryption/bin
TestBO4E-dotnet-Encryption/TestResults
TestBO4E-dotnet-Extensions/bin
TestBO4E-dotnet-Extensions/TestResults
TestBO4E-dotnet-Reporting/bin
TestBO4E-dotnet-Reporting/TestResults/
82 changes: 45 additions & 37 deletions BO4E-dotnet.Encryption/Anonymizer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +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 @@ -116,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 @@ -128,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 @@ -144,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 @@ -184,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 @@ -200,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 @@ -228,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 @@ -253,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 @@ -336,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 @@ -415,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
23 changes: 19 additions & 4 deletions BO4E-dotnet.Encryption/AsymmetricEncrypter.cs
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
using System;
using System.Text;

using BO4E.BO;

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

using Sodium;

namespace BO4E.Extensions.Encryption
Expand All @@ -19,8 +21,10 @@ public class AsymmetricEncrypter : Encrypter
/// <param name="publicKey">public key</param>
public AsymmetricEncrypter(byte[] privateKey, byte[] publicKey)
{
this.privateKey = privateKey;
this.ownPublicKey = publicKey;
this.ownPublicKey = new byte[publicKey.Length];
this.privateKey = new byte[privateKey.Length];
privateKey.CopyTo(this.privateKey, 0);
publicKey.CopyTo(this.ownPublicKey, 0);
}
/// <summary>
/// Instantiate with private and public key
Expand Down Expand Up @@ -105,10 +109,21 @@ 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);
}

public override T Decrypt<T>(EncryptedObject encryptedObject)
{
EncryptedObjectPublicKeyBox eo = (EncryptedObjectPublicKeyBox)(encryptedObject);// (EncryptedObjectPublicKeyBox)BoMapper.MapObject("EncryptedObjectPublicKeyBox", JObject.FromObject(encryptedObject));
if (eo == null)
{
return (T)null;
}
string plainString = Decrypt(eo.CipherText, eo.PublicKey, eo.Nonce);
return JsonConvert.DeserializeObject<T>(plainString);
}

public override void Dispose()
{
if (privateKey != null)
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; }
}
}
Loading

0 comments on commit e5123a4

Please sign in to comment.