/// <summary> /// B.1.1 from FIPS 186-4. This is equivalent to B.1.2, the other KeyGeneration method. /// </summary> /// <param name="domainParameters"></param> /// <returns></returns> public FfcKeyPairGenerateResult GenerateKeyPair(FfcDomainParameters domainParameters) { var L = new BitString(domainParameters.P).BitLength; var N = new BitString(domainParameters.Q).BitLength; // Shouldn't really be necessary but just in case if (!DSAHelper.VerifyLenPair(L, N)) { return(new FfcKeyPairGenerateResult("Invalid L, N pair")); } var rand = new Random800_90(); var c = rand.GetRandomBitString(N + 64).ToPositiveBigInteger(); var x = (c % (domainParameters.Q - 1)) + 1; var y = BigInteger.ModPow(domainParameters.G, x, domainParameters.P); return(new FfcKeyPairGenerateResult(new FfcKeyPair(x, y))); }
/// <summary> /// A.1.2.1.1 Get a first seed value /// </summary> /// <param name="N"></param> /// <param name="seedLen"></param> /// <returns></returns> private BigInteger GetFirstSeed(int L, int N, int seedLen) { if (!DSAHelper.VerifyLenPair(L, N)) { return(0); } if (seedLen < N) { return(0); } BitString firstSeed; do { firstSeed = _entropy.GetEntropy(seedLen); } while (firstSeed.ToPositiveBigInteger() < NumberTheory.Pow2(N - 1)); return(firstSeed.ToPositiveBigInteger()); }
/// <summary> /// A.1.2.1.2 /// </summary> /// <param name="L"></param> /// <param name="N"></param> /// <param name="firstSeed"></param> /// <returns></returns> private PQGenerateResult Generate(int L, int N, BigInteger firstSeed) { // 1 if (!DSAHelper.VerifyLenPair(L, N)) { return(new PQGenerateResult("Bad L, N pair")); } // 2 var qResult = PrimeGen186_4.ShaweTaylorRandomPrime(N, firstSeed, _sha); if (!qResult.Success) { return(new PQGenerateResult("Failed to generate q from ShaweTaylor")); } var q = qResult.Prime; var qSeed = qResult.PrimeSeed; var qCounter = qResult.PrimeGenCounter; // 3 var pLen = L.CeilingDivide(2) + 1; var pResult = PrimeGen186_4.ShaweTaylorRandomPrime(pLen, qSeed, _sha); if (!pResult.Success) { return(new PQGenerateResult("Failed to generate p0 from ShaweTaylor")); } var p0 = pResult.Prime; var pSeed = pResult.PrimeSeed; var pCounter = pResult.PrimeGenCounter; // 4, 5 var outLen = _sha.HashFunction.OutputLen; var iterations = L.CeilingDivide(outLen) - 1; var oldCounter = pCounter; // 6, 7 BigInteger x = 0; for (var i = 0; i <= iterations; i++) { x += _sha.HashNumber(pSeed + i).ToBigInteger() * NumberTheory.Pow2(i * outLen); } // 8 pSeed += iterations + 1; // 9 x = NumberTheory.Pow2(L - 1) + (x % NumberTheory.Pow2(L - 1)); // 10 var t = x.CeilingDivide(2 * q * p0); do { // 11 if (2 * t * q * p0 + 1 > NumberTheory.Pow2(L)) { t = NumberTheory.Pow2(L - 1).CeilingDivide(2 * q * p0); } // 12, 13 var p = 2 * t * q * p0 + 1; pCounter++; // 14, 15 BigInteger a = 0; for (var i = 0; i <= iterations; i++) { a += _sha.HashNumber(pSeed + i).ToBigInteger() * NumberTheory.Pow2(i * outLen); } // 16 pSeed += iterations + 1; // 17 a = 2 + (a % (p - 3)); // 18 var z = BigInteger.ModPow(a, 2 * t * q, p); // 19 if (1 == NumberTheory.GCD(z - 1, p) && 1 == BigInteger.ModPow(z, p0, p)) { return(new PQGenerateResult(p, q, new DomainSeed(firstSeed, pSeed, qSeed), new Counter(pCounter, qCounter))); } // 20 if (pCounter > 4 * L + oldCounter) { return(new PQGenerateResult("Too many iterations")); } // 21 t++; // 22 } while (true); }
/// <summary> /// A.1.2.2 /// </summary> /// <param name="p"></param> /// <param name="q"></param> /// <param name="seed"></param> /// <param name="count"></param> /// <returns></returns> public PQValidateResult Validate(BigInteger p, BigInteger q, DomainSeed seed, Counter count) { // 0, domain type check if (seed.Mode != PrimeGenMode.Provable && count.Mode != PrimeGenMode.Provable) { return(new PQValidateResult("Invalid DomainSeed and Counter")); } // 1, 2 var L = new BitString(p).BitLength; var N = new BitString(q).BitLength; // 3 if (!DSAHelper.VerifyLenPair(L, N)) { return(new PQValidateResult("Invalid L, N pair")); } // 4 if (seed.Seed < NumberTheory.Pow2(N - 1)) { return(new PQValidateResult("Bad first seed")); } // 5 if (NumberTheory.Pow2(N) <= q) { return(new PQValidateResult("Bad q, too small")); } // 6 if (NumberTheory.Pow2(L) <= p) { return(new PQValidateResult("Bad p, too large")); } // 7 if ((p - 1) % q != 0) { return(new PQValidateResult("p - 1 % q != 0, bad values")); } // 8 var computed_result = Generate(L, N, seed.Seed); if (!computed_result.Success) { return(new PQValidateResult("Failed to generate p and q")); } if (q != computed_result.Q || seed.QSeed != computed_result.Seed.QSeed || count.QCount != computed_result.Count.QCount) { return(new PQValidateResult("Failed to generate given q")); } if (p != computed_result.P || seed.PSeed != computed_result.Seed.PSeed || count.PCount != computed_result.Count.PCount) { return(new PQValidateResult("Failed to generate given p")); } return(new PQValidateResult()); }
/// <summary> /// A.1.1.2 from FIPS 186-4 /// </summary> /// <param name="L"></param> /// <param name="N"></param> /// <param name="seedLen"></param> /// <returns></returns> public PQGenerateResult Generate(int L, int N, int seedLen) { // 1. Check L/N pair if (!DSAHelper.VerifyLenPair(L, N)) { return(new PQGenerateResult("Invalid L, N pair")); } // 2. Check seedLen if (seedLen < N) { return(new PQGenerateResult("Invalid seedLen")); } // 3, 4 Compute n, b var outLen = _sha.HashFunction.OutputLen; var n = L.CeilingDivide(outLen) - 1; var b = L - 1 - (n * outLen); do { BigInteger seed, q; do { // 5. Get random seed seed = _entropy.GetEntropy(seedLen).ToPositiveBigInteger(); // 6. Hash seed var U = _sha.HashNumber(seed).ToBigInteger() % NumberTheory.Pow2(N - 1); // 7. Compute q q = NumberTheory.Pow2(N - 1) + U + 1 - (U % 2); // Check if q is prime, if not go back to 5, assume highest security strength } while (!NumberTheory.MillerRabin(q, DSAHelper.GetMillerRabinIterations(L, N))); // 10, 11 Compute p var offset = 1; var upperBound = (4 * L - 1); for (var ctr = 0; ctr <= upperBound; ctr++) { // 11.1, 11.2 var W = _sha.HashNumber(seed + offset).ToBigInteger(); for (var j = 1; j < n; j++) { W += _sha.HashNumber(seed + offset + j).ToBigInteger() * NumberTheory.Pow2(j * outLen); } W += (_sha.HashNumber(seed + offset + n).ToBigInteger() % NumberTheory.Pow2(b)) * NumberTheory.Pow2(n * outLen); // 11.3 var X = W + NumberTheory.Pow2(L - 1); // 11.4 var c = X % (2 * q); // 11.5 var p = X - (c - 1); // 11.6, 11.7, 11.8 if (p >= NumberTheory.Pow2(L - 1)) { // Check if p is prime, if so return if (NumberTheory.MillerRabin(p, DSAHelper.GetMillerRabinIterations(L, N))) { return(new PQGenerateResult(p, q, new DomainSeed(seed), new Counter(ctr))); } } // 11.9 offset += n + 1; } // 12 } while (true); }
/// <summary> /// A.1.1.3 from FIPS 186-4 /// </summary> /// <param name="p"></param> /// <param name="q"></param> /// <param name="seed"></param> /// <param name="count"></param> /// <returns></returns> public PQValidateResult Validate(BigInteger p, BigInteger q, DomainSeed seed, Counter count) { if (seed.Mode != PrimeGenMode.Probable && count.Mode != PrimeGenMode.Probable) { return(new PQValidateResult("Invalid DomainSeed and Counter")); } // 1, 2 var L = new BitString(p).BitLength; var N = new BitString(q).BitLength; // 3 if (!DSAHelper.VerifyLenPair(L, N)) { return(new PQValidateResult("Invalid L, N pair")); } // 4 if (count.Count > (4 * L - 1)) { return(new PQValidateResult("Invalid counter")); } // 5, 6 /* * Appending 0s to the bitstring representation of the seed as to make it mod 32 (if it isn't already), as this is the mod of the original seed that is hashed. * In instances (as an example) when the chosen seed starts with eight zero bits in a row, the biginteger representation of said bitstring is 1 byte smaller than it should be, * thus failing the check that it is at least the length of N */ var seedBitString = new BitString(seed.Seed); if (seedBitString.BitLength % 32 != 0) { seedBitString = BitString.ConcatenateBits(BitString.Zeroes(32 - seedBitString.BitLength % 32), seedBitString); } var seedLen = seedBitString.BitLength; if (seedLen < N) { return(new PQValidateResult("Invalid seed")); } // 7 var U = _sha.HashNumber(seed.Seed).ToBigInteger() % NumberTheory.Pow2(N - 1); // 8 var computed_q = NumberTheory.Pow2(N - 1) + U + 1 - (U % 2); // 9 if (!NumberTheory.MillerRabin(computed_q, DSAHelper.GetMillerRabinIterations(L, N)) || computed_q != q) { return(new PQValidateResult("Q not prime, or doesn't match expected value")); } // 10, 11, 12 var outLen = _sha.HashFunction.OutputLen; var n = L.CeilingDivide(outLen) - 1; var b = L - 1 - (n * outLen); var offset = 1; // 13 BigInteger computed_p = 0; int i; for (i = 0; i <= count.Count; i++) { // 13.1, 13.2 var W = _sha.HashNumber(seed.Seed + offset).ToBigInteger(); for (var j = 1; j < n; j++) { W += (_sha.HashNumber(seed.Seed + offset + j).ToBigInteger()) * NumberTheory.Pow2(j * outLen); } W += ((_sha.HashNumber(seed.Seed + offset + n).ToBigInteger()) % NumberTheory.Pow2(b)) * NumberTheory.Pow2(n * outLen); // 13.3 var X = W + NumberTheory.Pow2(L - 1); // 13.4 var c = X % (2 * q); // 13.5 computed_p = X - (c - 1); // 13.6, 13.7, 13.8 if (computed_p >= NumberTheory.Pow2(L - 1)) { // Check if p is prime, if so return if (NumberTheory.MillerRabin(computed_p, DSAHelper.GetMillerRabinIterations(L, N))) { break; } } // 13.9 offset += n + 1; } // 14 if (i != count.Count || computed_p != p || !NumberTheory.MillerRabin(computed_p, DSAHelper.GetMillerRabinIterations(L, N))) { return(new PQValidateResult($"Invalid p value or counter. computed_p = {new BitString(computed_p).ToHex()}")); } // 15 return(new PQValidateResult()); }