From 21dbe115af77e11d5b39d513751f8a7b4ea9a3a8 Mon Sep 17 00:00:00 2001 From: bazzilic Date: Sun, 25 Sep 2022 15:46:52 +0800 Subject: [PATCH 1/2] added new ctor that can take pre-existing modulus --- README.md | 2 +- src/Aprismatic.Paillier/Paillier.cs | 85 +++++++++++++------- src/Aprismatic.Paillier/PaillierDecryptor.cs | 4 +- src/Aprismatic.Paillier/PaillierEncryptor.cs | 8 +- 4 files changed, 61 insertions(+), 38 deletions(-) diff --git a/README.md b/README.md index b6cf5c9..63bf84a 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# PaillierExt +# Paillier ![Test .NET (Windows)](https://github.com/aprismatic/paillier/workflows/Test%20.NET%20(Windows)/badge.svg?branch=master) diff --git a/src/Aprismatic.Paillier/Paillier.cs b/src/Aprismatic.Paillier/Paillier.cs index ee06f5d..0543186 100644 --- a/src/Aprismatic.Paillier/Paillier.cs +++ b/src/Aprismatic.Paillier/Paillier.cs @@ -31,7 +31,26 @@ 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); + } + + public Paillier(BigInteger p, BigInteger q) + { + LegalKeySizesValue = new[] { new KeySizes(384, 1088, 8) }; + + var N = p * q; + KeySizeValue = N.BitCount(); + + 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."); + + // TODO: do we need to check that p and q bit count is half of N bit count? + + keyStruct = CreateKeyPair(PaillierKeyDefaults.DefaultMaxPlaintextBits, PaillierKeyDefaults.DefaultPlaintextDecPlace, p, q, N); Encryptor = new PaillierEncryptor(keyStruct); Decryptor = new PaillierDecryptor(keyStruct); @@ -61,48 +80,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 diff --git a/src/Aprismatic.Paillier/PaillierDecryptor.cs b/src/Aprismatic.Paillier/PaillierDecryptor.cs index b8db709..8620b18 100644 --- a/src/Aprismatic.Paillier/PaillierDecryptor.cs +++ b/src/Aprismatic.Paillier/PaillierDecryptor.cs @@ -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 N²) - 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; } diff --git a/src/Aprismatic.Paillier/PaillierEncryptor.cs b/src/Aprismatic.Paillier/PaillierEncryptor.cs index 86868d3..f5a61a0 100644 --- a/src/Aprismatic.Paillier/PaillierEncryptor.cs +++ b/src/Aprismatic.Paillier/PaillierEncryptor.cs @@ -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 N² 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 N² var Gm = (_keyStruct.N * encodedMessage + BigInteger.One) % _keyStruct.NSquare; var Gm_Neg = (_keyStruct.N * encodedMessage_neg + BigInteger.One) % _keyStruct.NSquare; From d332c5d4be18e826c4018ca9e88777930fb754bf Mon Sep 17 00:00:00 2001 From: bazzilic Date: Sun, 25 Sep 2022 18:14:52 +0800 Subject: [PATCH 2/2] fix for decode to work faster --- src/Aprismatic.Paillier/Paillier.cs | 27 +++++--------------- src/Aprismatic.Paillier/PaillierKeyStruct.cs | 7 +++++ 2 files changed, 14 insertions(+), 20 deletions(-) diff --git a/src/Aprismatic.Paillier/Paillier.cs b/src/Aprismatic.Paillier/Paillier.cs index 0543186..de3be81 100644 --- a/src/Aprismatic.Paillier/Paillier.cs +++ b/src/Aprismatic.Paillier/Paillier.cs @@ -38,24 +38,6 @@ public Paillier(int keySize) // TODO: Constructor should probably optionally acc Decryptor = new PaillierDecryptor(keyStruct); } - public Paillier(BigInteger p, BigInteger q) - { - LegalKeySizesValue = new[] { new KeySizes(384, 1088, 8) }; - - var N = p * q; - KeySizeValue = N.BitCount(); - - 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."); - - // TODO: do we need to check that p and q bit count is half of N bit count? - - keyStruct = CreateKeyPair(PaillierKeyDefaults.DefaultMaxPlaintextBits, PaillierKeyDefaults.DefaultPlaintextDecPlace, p, q, N); - - Encryptor = new PaillierEncryptor(keyStruct); - Decryptor = new PaillierDecryptor(keyStruct); - } - public Paillier(PaillierParameters prms) // TODO: Consolidate constructors in one method { LegalKeySizesValue = new[] { new KeySizes(384, 1088, 8) }; @@ -169,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; } diff --git a/src/Aprismatic.Paillier/PaillierKeyStruct.cs b/src/Aprismatic.Paillier/PaillierKeyStruct.cs index fda81d2..053584c 100644 --- a/src/Aprismatic.Paillier/PaillierKeyStruct.cs +++ b/src/Aprismatic.Paillier/PaillierKeyStruct.cs @@ -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; @@ -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;