private BouncyCastle.FpFieldElement GetX( byte[] input, BouncyCastle.FpCurve curve, int index, int counter) { int numIterations = (int)System.Math.Ceiling( (double)(curve.Q.BitLength / 8) / (double)hashByteSize); byte[] digest = new byte[numIterations * hashByteSize]; for (int iteration = 0; iteration < numIterations; iteration++) { byte[] hashInput = ProtocolHelper.Concatenate( input, encoding.GetBytes( index.ToString() + counter.ToString() + iteration.ToString())); hash.HashWithoutFormatting(hashInput); Array.Copy(hash.Digest, 0, digest, hashByteSize * iteration, hashByteSize); } BCBigInt x = new BCBigInt(1, digest).Mod(curve.Q); return(curve.FromBigInteger(x) as BouncyCastle.FpFieldElement); }
/// <summary> /// Verifies that e is an element of the group. /// </summary> /// <param name="e">The element to test.</param> /// <exception cref="InvalidUProveArtifactException"> /// Thrown if i is not in the group.</exception> public override void ValidateGroupElement(GroupElement e) { SubgroupGroupElementBCImpl sge = e as SubgroupGroupElementBCImpl; if (sge == null) { throw new InvalidUProveArtifactException( "Invalid group element (wrong construction)"); } // verify that 1 < g < p if (sge.i <= BCBigInt.One || sge.i >= pValue) { throw new InvalidUProveArtifactException( "Invalid group element (out of range)"); } // verify that g^q mod p = 1 BCBigInt modpow = sge.i.ModPow(qValue, pValue); if (sge.i.ModPow(qValue, pValue) != BCBigInt.One) { throw new InvalidUProveArtifactException( "Invalid group element (i^Q mod P != 1)"); } }
/// <summary> /// Returns a random BigInteger x such that 0 <= x < max. /// </summary> /// <param name="max">Maximal value (exclusive).</param> /// <returns>A random BigInteger.</returns> private static BCBigInt GetRandomValue(BCBigInt max) { if (max <= BCBigInt.Zero) { throw new ArgumentException("max must be greater than zero"); } int length = max.ToByteArray().Length; BCBigInt bi; do { // randomly generate an array with a trailing 0 byte byte[] randomBytes = new byte[length]; rngCSP.GetBytes(randomBytes); // generate a random positive BigInteger bi = new BCBigInt(1, randomBytes); } while ( // Make sure the value is smaller than max to avoid // bias in the RNG. This would open up // attacks on the system (see Bleichenbacher's attack // on FIPS 186's RNG). This technique is equivalent to // NIST SP 800-90 (draft)'s "simple discard method" // (section B.5.1.1). (bi >= max)); return(bi); }
/// <summary> /// Generates a random Zq element. /// </summary> /// <param name="nonZero">True to return a non-zero element.</param> /// <param name="maxBitLength">Maximum lenght of the random element, or -1 for full size elements.</param> /// <returns>A random Zq element.</returns> public override FieldZqElement GetRandomElement(bool nonZero, int maxBitLength = -1) { BCBigInt element = null; do { BCBigInt max = null; if (maxBitLength > 0) { if (TwoToTheX.ContainsKey(maxBitLength)) { max = TwoToTheX[maxBitLength]; } else { max = BCBigInt.Two.Pow(maxBitLength); TwoToTheX.Add(maxBitLength, max); } } else { max = this.modulus; } element = GetRandomValue(max); } while (nonZero && element == 0); return(new FieldZqElementBCImpl(element, this)); }
/// <summary> /// Constructs a FieldZqImpl. /// </summary> /// <param name="modulus">The field modulus</param> public FieldZqBCImpl(byte[] modulus) { if (modulus == null) throw new ArgumentNullException("modulus"); this.modulusBytes = modulus; this.modulus = new BCBigInt(1, modulus); Zero = new FieldZqElementBCImpl(BCBigInt.Zero, this); One = new FieldZqElementBCImpl(BCBigInt.One, this); }
/// <summary> /// Constructs a new SubgroupGroupElement. /// </summary> /// <param name="i">The element value.</param> /// <param name="p">The modulus.</param> internal SubgroupGroupElementBCImpl(BCBigInt i, BCBigInt p) { if (i == null || p == null) { throw new ArgumentNullException(); } this.i = i; this.p = p; }
/// <summary> /// Constructs a FieldZqImpl. /// </summary> /// <param name="modulus">The field modulus</param> public FieldZqBCImpl(byte[] modulus) { if (modulus == null) { throw new ArgumentNullException("modulus"); } this.modulusBytes = modulus; this.modulus = new BCBigInt(1, modulus); Zero = new FieldZqElementBCImpl(BCBigInt.Zero, this); One = new FieldZqElementBCImpl(BCBigInt.One, this); }
/// <summary> /// Verifies that the group is correctly constructed. /// </summary> /// <exception cref="InvalidUProveArtifactException"> /// Thrown if the group parameters are invalid.</exception> public override void Verify() { // verify that p is an odd prime if (pValue <= BCBigInt.Two || !pValue.IsProbablePrime(ProtocolHelper.PrimalityTestingCertainty)) { throw new InvalidUProveArtifactException( "Invalid group: P is not an odd prime"); } // verify that q is an odd prime if (qValue <= BCBigInt.Two || !qValue.IsProbablePrime(ProtocolHelper.PrimalityTestingCertainty)) { throw new InvalidUProveArtifactException( "Invalid group: Q is not an odd prime"); } // verify that q divides p - 1 BCBigInt remainder; BCBigInt.DivRem(pValue - BCBigInt.One, qValue, out remainder); if (remainder != BCBigInt.Zero) { throw new InvalidUProveArtifactException( "Invalid group: Q does not divide P-1)"); } // verify that g is a group element try { ValidateGroupElement(G); } catch (InvalidUProveArtifactException) { throw new InvalidUProveArtifactException( "Invalid group: G is not a group element"); } }
/// <summary> /// Internal only constructor for a Bouncy Castle based field Zq element. /// </summary> /// <param name="i">The value of the element.</param> /// <param name="field">The field to which the element belongs.</param> internal FieldZqElementBCImpl(BCBigInt i, FieldZqBCImpl field) { this.i = i; this.field = field; }
/// <summary> /// Returns a random BigInteger x such that 0 <= x < max. /// </summary> /// <param name="max">Maximal value (exclusive).</param> /// <returns>A random BigInteger.</returns> private static BCBigInt GetRandomValue(BCBigInt max) { if (max <= BCBigInt.Zero) { throw new ArgumentException("max must be greater than zero"); } int length = max.ToByteArray().Length; BCBigInt bi; do { // randomly generate an array with a trailing 0 byte byte[] randomBytes = new byte[length]; rngCSP.GetBytes(randomBytes); // generate a random positive BigInteger bi = new BCBigInt(1, randomBytes); } while ( // Make sure the value is smaller than max to avoid // bias in the RNG. This would open up // attacks on the system (see Bleichenbacher's attack // on FIPS 186's RNG). This technique is equivalent to // NIST SP 800-90 (draft)'s "simple discard method" // (section B.5.1.1). (bi >= max)); return bi; }
/// <summary> /// This method implements the method defined in recommended parameters spec /// </summary> public override GroupElement DeriveElement( byte[] context, byte index, out int counter) { byte[] ggen = new byte[] { (byte)0x67, (byte)0x67, (byte)0x65, (byte)0x6E }; string hashAlg = null; int bitlength = qValue.BitLength; if (bitlength >= 512) { hashAlg = "SHA-512"; } else if (bitlength >= 256) { hashAlg = "SHA-256"; } else if (bitlength >= 160) { hashAlg = "SHA1"; } else { throw new ArgumentException("q is too small"); } HashFunction hash = new HashFunction(hashAlg); // references to "step x" in comments refer to alg from apendix A.2.3 of FIPS 186-3 // int N = this.q.BitLength; // step 2 (usused) BCBigInt e = (pValue - BCBigInt.One) / qValue; // step 3 // prepare U array = context || "ggen" || index || count byte[] contextBytes = (context == null ? new byte[] {} : context); int contextLength = (contextBytes == null ? 0 : contextBytes.Length); byte[] U = new byte[contextLength + ggen.Length + 2]; int arrayIndex = 0; if (contextLength > 0) { Array.Copy(contextBytes, 0, U, arrayIndex, contextLength); arrayIndex += contextLength; } Array.Copy(ggen, 0, U, arrayIndex, ggen.Length); U[U.Length - 2] = index; byte count = 0; // step 4 BCBigInt g = BCBigInt.Zero; while (g < BCBigInt.Two) // step 10 { if (count == 255) { throw new InvalidUProveArtifactException( "can't derive an element; count exceeded"); } count++; // step 5 // complete U array U[U.Length - 1] = count; // step 7 hash.HashWithoutFormatting(U); // BUGBUG: is that ok, will that wrap correctly? BCBigInt W = new BCBigInt(1, hash.Digest); // step 8 g = W.ModPow(e, pValue); // step 9 } counter = count; return(new SubgroupGroupElementBCImpl(g, pValue)); }
/// <summary> /// This method implements the method defined in recommended parameters spec /// </summary> public override GroupElement DeriveElement( byte[] context, byte index, out int counter) { byte[] ggen = new byte[] { (byte)0x67, (byte)0x67, (byte)0x65, (byte)0x6E }; string hashAlg = null; int bitlength = qValue.BitLength; if (bitlength >= 512) { hashAlg = "SHA-512"; } else if (bitlength >= 256) { hashAlg = "SHA-256"; } else if (bitlength >= 160) { hashAlg = "SHA1"; } else { throw new ArgumentException("q is too small"); } HashFunction hash = new HashFunction(hashAlg); // references to "step x" in comments refer to alg from apendix A.2.3 of FIPS 186-3 // int N = this.q.BitLength; // step 2 (usused) BCBigInt e = (pValue - BCBigInt.One) / qValue; // step 3 // prepare U array = context || "ggen" || index || count byte[] contextBytes = (context == null ? new byte[] {} : context); int contextLength = (contextBytes == null ? 0 : contextBytes.Length); byte[] U = new byte[contextLength + ggen.Length + 2]; int arrayIndex = 0; if (contextLength > 0) { Array.Copy(contextBytes, 0, U, arrayIndex, contextLength); arrayIndex += contextLength; } Array.Copy(ggen, 0, U, arrayIndex, ggen.Length); U[U.Length - 2] = index; byte count = 0; // step 4 BCBigInt g = BCBigInt.Zero; while (g < BCBigInt.Two) // step 10 { if (count == 255) { throw new InvalidUProveArtifactException( "can't derive an element; count exceeded"); } count++; // step 5 // complete U array U[U.Length - 1] = count; // step 7 hash.HashWithoutFormatting(U); // BUGBUG: is that ok, will that wrap correctly? BCBigInt W = new BCBigInt(1, hash.Digest); // step 8 g = W.ModPow(e, pValue); // step 9 } counter = count; return new SubgroupGroupElementBCImpl(g, pValue); }
private BouncyCastle.FpFieldElement GetX( byte[] input, BouncyCastle.FpCurve curve, int index, int counter) { int numIterations = (int)System.Math.Ceiling( (double)(curve.Q.BitLength / 8) / (double)hashByteSize); byte[] digest = new byte[numIterations * hashByteSize]; for (int iteration = 0; iteration < numIterations; iteration++) { byte[] hashInput = ProtocolHelper.Concatenate( input, encoding.GetBytes( index.ToString() + counter.ToString() + iteration.ToString())); hash.HashWithoutFormatting(hashInput); Array.Copy(hash.Digest, 0, digest, hashByteSize * iteration, hashByteSize); } BCBigInt x = new BCBigInt(1, digest).Mod(curve.Q); return curve.FromBigInteger(x) as BouncyCastle.FpFieldElement; }