Skip to content

Commit

Permalink
Performance optimizations (#25)
Browse files Browse the repository at this point in the history
* a bunch of optimisations

* small fixes

* deps version bump
  • Loading branch information
bazzilic authored Jun 2, 2020
1 parent e61f043 commit 8d95416
Show file tree
Hide file tree
Showing 14 changed files with 247 additions and 331 deletions.
6 changes: 3 additions & 3 deletions src/Aprismatic.PaillierExt/Aprismatic.PaillierExt.csproj
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<TargetFramework>netstandard2.1</TargetFramework>
<Description>Extension for the .NET Framework cryptography subsystem, which introduces the Paillier public key cryptosystem with support for homomorphic addition.</Description>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Aprismatic.BigFraction" Version="0.1.2" />
<PackageReference Include="Aprismatic.BigIntegerExt" Version="0.1.5" />
<PackageReference Include="Aprismatic.BigFraction" Version="0.1.3" />
<PackageReference Include="Aprismatic.BigIntegerExt" Version="0.1.6" />
<PackageReference Include="Aprismatic.PaillierExt.Homomorphism" Version="0.10.2" />
</ItemGroup>
</Project>
212 changes: 100 additions & 112 deletions src/Aprismatic.PaillierExt/Paillier.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,105 +7,129 @@

namespace Aprismatic.PaillierExt
{
public class Paillier : AsymmetricAlgorithm
public class Paillier : AsymmetricAlgorithm, IDisposable
{
private PaillierKeyStruct keyStruct;
private readonly PaillierKeyStruct keyStruct;
private readonly PaillierEncryptor encryptor;
private readonly PaillierDecryptor decryptor;

public PaillierKeyStruct KeyStruct
public Paillier(int keySize)
{
get
{
if (NeedToGenerateKey())
{
CreateKeyPair(KeySizeValue);
}
return keyStruct;
}
set => keyStruct = value;
LegalKeySizesValue = new[] { new KeySizes(384, 1088, 8) };
KeySizeValue = keySize;
keyStruct = CreateKeyPair();
encryptor = new PaillierEncryptor(keyStruct);
decryptor = new PaillierDecryptor(keyStruct);
}

public Paillier()
public Paillier(PaillierParameters prms)
{
keyStruct = new PaillierKeyStruct
{
N = BigInteger.Zero,
G = BigInteger.Zero,
Lambda = BigInteger.Zero,
Miu = BigInteger.Zero
};
LegalKeySizesValue = new[] { new KeySizes(384, 1088, 8) };

// set the default key size value
KeySizeValue = 384;
keyStruct = new PaillierKeyStruct(
new BigInteger(prms.N),
new BigInteger(prms.G),
(prms.Lambda?.Length ?? 0) > 0 ? new BigInteger(prms.Lambda) : BigInteger.Zero,
(prms.Miu?.Length ?? 0) > 0 ? new BigInteger(prms.Miu) : BigInteger.Zero
);

// set the range of legal keys
LegalKeySizesValue = new[] { new KeySizes(384, 1088, 8) };
KeySizeValue = keyStruct.NLength * 8;

encryptor = new PaillierEncryptor(keyStruct);
decryptor = new PaillierDecryptor(keyStruct);
}

private bool NeedToGenerateKey()
public Paillier(string Xml)
{
return keyStruct.N == 0
&& keyStruct.G == 0;
LegalKeySizesValue = new[] { new KeySizes(384, 1088, 8) };

var prms = new PaillierParameters();
var keyValues = XDocument.Parse(Xml).Element("PaillierKeyValue");
prms.N = Convert.FromBase64String((String) keyValues.Element("N") ?? "");
prms.G = Convert.FromBase64String((String) keyValues.Element("G") ?? "");
prms.Lambda = Convert.FromBase64String((String) keyValues.Element("Lambda") ?? "");
prms.Miu = Convert.FromBase64String((String) keyValues.Element("Miu") ?? "");

keyStruct = new PaillierKeyStruct(
new BigInteger(prms.N),
new BigInteger(prms.G),
new BigInteger(prms.Lambda),
new BigInteger(prms.Miu)
);

KeySizeValue = keyStruct.NLength * 8;

encryptor = new PaillierEncryptor(keyStruct);
decryptor = new PaillierDecryptor(keyStruct);
}

public int MaxPlaintextBits() => PaillierKeyStruct.MaxPlaintextBits;
public BigInteger PlaintextExp => PaillierKeyStruct.PlaintextExp;
public int GetPlaintextDecPlace() => PaillierKeyStruct.PlaintextDecPlace;

// TODO: check again for Miu
private void CreateKeyPair(int pKeyStrength)
private PaillierKeyStruct CreateKeyPair()
{
BigInteger N, Lambda, G, Miu;

// create the large prime number, p and q
// p and q are assumed to have the same bit length (512 bit each, so that N is 1024)
// if N length is not the same to KeySize, will regenerate p and q which will make a new N
using (var rng = RandomNumberGenerator.Create())
// p and q are assumed to have the same bit length (e.g., 192 bit each, so that N is 384)
// if N length is not the same to keySize, will regenerate p and q which will make a new N
using var rng = RandomNumberGenerator.Create();
var p = new BigInteger();
var q = new BigInteger();
var halfKeyStrength = KeySizeValue >> 1; // div 2
do
{
var p = new BigInteger();
var q = new BigInteger();
do
{
p = p.GenPseudoPrime(pKeyStrength / 2, 16, rng);
q = q.GenPseudoPrime(pKeyStrength / 2, 16, rng);

// compute N
// N = p*q
keyStruct.N = p * q;
} while (KeyStruct.getNLength() != pKeyStrength / 8);

// compute G
// First option: g is random in Z*(n^2)

// Second option: g = n + 1
keyStruct.G = keyStruct.N + 1;

// compute lambda
// lambda = lcm(p-1, q-1) = (p-1)*(q-1)/gcd(p-1, q-1)
// or simpler variant, lambda = (p-1)(q-1), since p and q have same length
keyStruct.Lambda = (p - 1) * (q - 1);

// Miu = (L(g^lambda mod NSq))^-1 mod n
// or simple: Miu = lambda^-1 (mod n)
keyStruct.Miu = keyStruct.Lambda.ModInverse(keyStruct.N);
}
p = p.GenPseudoPrime(halfKeyStrength, 16, rng);
q = q.GenPseudoPrime(halfKeyStrength, 16, rng);

// compute N
// N = p*q
N = p * q;
} while (N.BitCount() < KeySizeValue - 7);

// compute G
// First option: G is random in Z*(N^2)
// Second option: G = N + 1
G = N + 1;

// compute lambda
// lambda = lcm(p-1, q-1) = (p-1)*(q-1)/gcd(p-1, q-1)
// or simpler variant, lambda = (p-1)(q-1), since p and q have same length
Lambda = (p - 1) * (q - 1);

// Miu = (L(g^lambda mod NSq))^-1 mod n
// or simple: Miu = lambda^-1 (mod n)
Miu = Lambda.ModInverse(N);

return new PaillierKeyStruct(N, G, Lambda, Miu);
}

public byte[] EncryptData(BigFraction message)
public byte[] EncryptDataOld(BigFraction message)
{
if (NeedToGenerateKey())
{
CreateKeyPair(KeySizeValue);
}

using (var encryptor = new PaillierEncryptor(keyStruct))
{
return encryptor.ProcessBigInteger(message);
return encryptor.ProcessBigIntegerOld(message);
}
}

public BigFraction DecryptData(byte[] p_data)
public byte[] EncryptData(BigFraction message)
{
if (NeedToGenerateKey())
{
CreateKeyPair(KeySizeValue);
}
var res = new byte[keyStruct.CiphertextBlocksize * 2];
encryptor.ProcessBigInteger(message, res.AsSpan());
return res;
}

public BigFraction DecryptDataOld(byte[] p_data)
{
var decryptor = new PaillierDecryptor(keyStruct);

return decryptor.ProcessByteBlockOld(p_data);
}

public BigFraction DecryptData(byte[] p_data)
{
return decryptor.ProcessByteBlock(p_data);
}

Expand Down Expand Up @@ -140,53 +164,12 @@ public override string ToXmlString(bool includePrivateParameters)
return sb.ToString();
}

public override void FromXmlString(string str)
{
var prms = new PaillierParameters();

var keyValues = XDocument.Parse(str).Element("PaillierKeyValue");

prms.N = Convert.FromBase64String((String)keyValues.Element("N") ?? "");
prms.G = Convert.FromBase64String((String)keyValues.Element("G") ?? "");
prms.Lambda = Convert.FromBase64String((String)keyValues.Element("Lambda") ?? "");
prms.Miu = Convert.FromBase64String((String)keyValues.Element("Miu") ?? "");

ImportParameters(prms);
}

public void ImportParameters(PaillierParameters parameters)
{
keyStruct.N = new BigInteger(parameters.N);
keyStruct.G = new BigInteger(parameters.G);

if (parameters.Lambda != null
&& parameters.Lambda.Length > 0
&& parameters.Miu != null
&& parameters.Miu.Length > 0)
{
keyStruct.Lambda = new BigInteger(parameters.Lambda);
keyStruct.Miu = new BigInteger(parameters.Miu);
}
else
{
keyStruct.Lambda = BigInteger.Zero;
keyStruct.Miu = BigInteger.Zero;
}

KeySizeValue = keyStruct.N.BitCount();
}

public PaillierParameters ExportParameters(bool includePrivateParams)
{
if (NeedToGenerateKey())
{
CreateKeyPair(KeySizeValue);
}

var prms = new PaillierParameters
{
N = keyStruct.N.ToByteArray(),
G = keyStruct.G.ToByteArray(),
G = keyStruct.G.ToByteArray()
};

// if required, include the private value, X
Expand All @@ -204,5 +187,10 @@ public PaillierParameters ExportParameters(bool includePrivateParams)

return prms;
}

public new void Dispose()
{
encryptor.Dispose();
}
}
}
14 changes: 0 additions & 14 deletions src/Aprismatic.PaillierExt/PaillierAbstractCipher.cs

This file was deleted.

33 changes: 24 additions & 9 deletions src/Aprismatic.PaillierExt/PaillierDecryptor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,31 +3,46 @@

namespace Aprismatic.PaillierExt
{
public class PaillierDecryptor : PaillierAbstractCipher
public class PaillierDecryptor
{
private readonly PaillierKeyStruct _keyStruct;

public PaillierDecryptor(PaillierKeyStruct keyStruct)
: base(keyStruct)
{ }
{
_keyStruct = keyStruct;
}

public BigFraction ProcessByteBlock(byte[] block)
public BigFraction ProcessByteBlockOld(byte[] block)
{
var block_half = new byte[block.Length / 2];
Array.Copy(block, block_half, block.Length / 2);
var bBlock = new BigInteger(block_half);

// calculate M
// m = (c^lambda(mod nsquare) - 1) / n * miu (mod n)
var m = (BigInteger.ModPow(bBlock, KeyStruct.Lambda, KeyStruct.NSquare) - 1) / KeyStruct.N * KeyStruct.Miu % KeyStruct.N;
var m = (BigInteger.ModPow(bBlock, _keyStruct.Lambda, _keyStruct.NSquare) - 1) / _keyStruct.N * _keyStruct.Miu % _keyStruct.N;

return Decode(m);
}

public BigFraction ProcessByteBlock(byte[] block)
{
var bBlock = new BigInteger(block.AsSpan(0, block.Length >> 1)); // div 2

// calculate M
// m = (c^lambda(mod nsquare) - 1) / n * miu (mod n)
var L = (BigInteger.ModPow(bBlock, _keyStruct.Lambda, _keyStruct.NSquare) - BigInteger.One) / _keyStruct.N;
var m = L * _keyStruct.Miu % _keyStruct.N;

return Decode(m);
}

private BigFraction Decode(BigInteger n)
{
var a = new BigFraction(n, KeyStruct.PlaintextExp);
a = a % (KeyStruct.MaxRawPlaintext + 1);
if ( a > KeyStruct.MaxRawPlaintext / 2)
a = a - KeyStruct.MaxRawPlaintext - 1;
var a = new BigFraction(n, PaillierKeyStruct.PlaintextExp);
a %= (PaillierKeyStruct.MaxRawPlaintext + 1);
if (a > PaillierKeyStruct.MaxEncryptableValue)
a = a - PaillierKeyStruct.MaxRawPlaintext - BigInteger.One;
return a;
}
}
Expand Down
Loading

0 comments on commit 8d95416

Please sign in to comment.