Skip to content

Commit

Permalink
Merge pull request #32 from aprismatic/pre-gen-prime
Browse files Browse the repository at this point in the history
Fix for Decode()
  • Loading branch information
bazzilic authored Sep 25, 2022
2 parents 2be2129 + d332c5d commit bc0ad1d
Show file tree
Hide file tree
Showing 5 changed files with 57 additions and 40 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# PaillierExt
# Paillier

![Test .NET (Windows)](https://github.com/aprismatic/paillier/workflows/Test%20.NET%20(Windows)/badge.svg?branch=master)

Expand Down
76 changes: 43 additions & 33 deletions src/Aprismatic.Paillier/Paillier.cs
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,8 @@ public Paillier(int keySize) // TODO: Constructor should probably optionally acc
if (!LegalKeySizesValue.Any(x => x.MinSize <= KeySizeValue && KeySizeValue <= x.MaxSize && (KeySizeValue - x.MinSize) % x.SkipSize == 0))
throw new ArgumentException("Key size is not supported by this algorithm.");

keyStruct = CreateKeyPair(PaillierKeyDefaults.DefaultMaxPlaintextBits, PaillierKeyDefaults.DefaultPlaintextDecPlace);
var (p, q, N) = GenPaillierModulus(KeySizeValue);
keyStruct = CreateKeyPair(PaillierKeyDefaults.DefaultMaxPlaintextBits, PaillierKeyDefaults.DefaultPlaintextDecPlace, p, q, N);

Encryptor = new PaillierEncryptor(keyStruct);
Decryptor = new PaillierDecryptor(keyStruct);
Expand Down Expand Up @@ -61,48 +62,52 @@ public Paillier(PaillierParameters prms) // TODO: Consolidate constructors in on
public Paillier(string Xml) : this(PaillierParameters.FromXml(Xml))
{ }

private PaillierKeyStruct CreateKeyPair(int maxptbits, int ptdecplaces) // TODO: This method should probably move to KeyStruct
private static PaillierKeyStruct CreateKeyPair(int maxptbits, int ptdecplaces, BigInteger p, BigInteger q, BigInteger N) // TODO: This method should probably move to KeyStruct
{
BigInteger N, Lambda, G, Mu;

// create the large prime number, p and q
// 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
{
do
{
p = p.GenPseudoPrime(halfKeyStrength, 16, rng);
} while (p.BitCount() != halfKeyStrength);

do
{
q = q.GenPseudoPrime(halfKeyStrength, 16, rng);
} while (q.BitCount() != halfKeyStrength);

N = p * q;
} while (N.BitCount() != KeySizeValue);
BigInteger Lambda, G, Mu;

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

// 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
// compute λ
// λ = lcm(p-1, q-1) = (p-1)*(q-1)/gcd(p-1, q-1)
// or simpler variant, λ = (p-1)(q-1), since p and q have same length
Lambda = (p - BigInteger.One) * (q - BigInteger.One);

// Mu = (L(g^lambda mod NSq))^-1 mod n
// or simple: Mu = lambda^-1 (mod n)
// µ = (L(g^λ mod N²))⁻¹ mod N
// or simple: µ = λ⁻¹ mod N
Mu = Lambda.ModInverse(N);

return new PaillierKeyStruct(N, G, Lambda, Mu, maxptbits, ptdecplaces);
}

// TODO: test this method as it becomes part of public API
public static (BigInteger p, BigInteger q, BigInteger N) GenPaillierModulus(int keySize)
{
// create two large prime numbers, p and q
// 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();

BigInteger N, p, q;

var halfKeyStrength = keySize >> 1; // div 2
do
{
do p = BigInteger.One.GenPseudoPrime(halfKeyStrength, 16, rng);
while (p.BitCount() != halfKeyStrength);

do q = BigInteger.One.GenPseudoPrime(halfKeyStrength, 16, rng);
while (q.BitCount() != halfKeyStrength);

N = p * q;
} while (N.BitCount() != keySize);

return (p, q, N);
}
#endregion

#region Encryption & Decryprtion
Expand Down Expand Up @@ -146,9 +151,14 @@ public BigInteger Encode(BigFraction msg) // TODO: Add tests now that this metho

public BigFraction Decode(BigInteger n) // TODO: Add tests now that this method is public
{
if (n > keyStruct.MaxEncryptableValueTimesExp)
n %= keyStruct.MaxRawPlaintextPlusOneTimesExp;

if (n > keyStruct.MaxEncryptableValueTimesExp)
n -= keyStruct.MaxRawPlaintextPlusOneTimesExp;

var a = new BigFraction(n, keyStruct.PlaintextExp);
while (a > keyStruct.MaxEncryptableValue)
a -= keyStruct.MaxRawPlaintext + BigFraction.One;

return a;
}

Expand Down
4 changes: 2 additions & 2 deletions src/Aprismatic.Paillier/PaillierDecryptor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,9 @@ public BigInteger ProcessByteBlock(byte[] block)
var bBlock = new BigInteger(block.AsSpan(0, block.Length >> 1)); // div 2

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

return m;
}
Expand Down
8 changes: 4 additions & 4 deletions src/Aprismatic.Paillier/PaillierEncryptor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,18 +19,18 @@ public void ProcessBigInteger(BigInteger encodedMessage, BigInteger encodedMessa
{
BigInteger R;

// generate random R ∊ Zn
// generate random r ∊ Zn
var NminusOne = _keyStruct.N - BigInteger.One;
do
{
R = BigInteger.Zero.GenRandomBits(_keyStruct.NBitCount, rng);
} while (R <= BigInteger.One || R >= NminusOne);

// ciphertext c = g^m * r^n mod n^2
// ciphertext c = g^m * r^N mod
var RN = BigInteger.ModPow(R, _keyStruct.N, _keyStruct.NSquare);

// if we use simple key generation (g = n + 1), we can use
// (n+1)^m = n*m + 1 mod n^2
// if we use simple key generation (g = N + 1), we can use
// (N+1)^m = N*m + 1 mod
var Gm = (_keyStruct.N * encodedMessage + BigInteger.One) % _keyStruct.NSquare;
var Gm_Neg = (_keyStruct.N * encodedMessage_neg + BigInteger.One) % _keyStruct.NSquare;

Expand Down
7 changes: 7 additions & 0 deletions src/Aprismatic.Paillier/PaillierKeyStruct.cs
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,9 @@ public PaillierKeyStruct(BigInteger n, BigInteger g, BigInteger lambda, BigInteg
MaxRawPlaintext = BigInteger.Pow(2, MaxPlaintextBits) - BigInteger.One;
MaxEncryptableValue = MaxRawPlaintext >> 1;

MaxRawPlaintextPlusOneTimesExp = (MaxRawPlaintext + BigInteger.One) * PlaintextExp;
MaxEncryptableValueTimesExp = MaxEncryptableValue * PlaintextExp;

Lambda = lambda;

Mu = mu;
Expand All @@ -49,8 +52,12 @@ public PaillierKeyStruct(BigInteger n, BigInteger g, BigInteger lambda, BigInteg
// HELPER VALUES
// These values are derived from the pub/priv key and precomputed for faster processing
public readonly BigInteger PlaintextExp;

public readonly BigInteger MaxRawPlaintext;
public readonly BigInteger MaxRawPlaintextPlusOneTimesExp;

public readonly BigInteger MaxEncryptableValue;
public readonly BigInteger MaxEncryptableValueTimesExp;

public readonly int NBitCount;
public readonly int NLength;
Expand Down

0 comments on commit bc0ad1d

Please sign in to comment.