/// <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()); }