123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394 |
- #if !BESTHTTP_DISABLE_ALTERNATE_SSL && (!UNITY_WEBGL || UNITY_EDITOR)
- #pragma warning disable
- using System;
- using System.Text;
- using BestHTTP.SecureProtocol.Org.BouncyCastle.Crypto;
- using BestHTTP.SecureProtocol.Org.BouncyCastle.Crypto.Macs;
- using BestHTTP.SecureProtocol.Org.BouncyCastle.Crypto.Parameters;
- using BestHTTP.SecureProtocol.Org.BouncyCastle.Crypto.Utilities;
- using BestHTTP.SecureProtocol.Org.BouncyCastle.Math;
- using BestHTTP.SecureProtocol.Org.BouncyCastle.Security;
- using BestHTTP.SecureProtocol.Org.BouncyCastle.Utilities;
- namespace BestHTTP.SecureProtocol.Org.BouncyCastle.Crypto.Agreement.JPake
- {
- /// <summary>
- /// Primitives needed for a J-PAKE exchange.
- ///
- /// The recommended way to perform a J-PAKE exchange is by using
- /// two JPAKEParticipants. Internally, those participants
- /// call these primitive operations in JPakeUtilities.
- ///
- /// The primitives, however, can be used without a JPAKEParticipant if needed.
- /// </summary>
- public abstract class JPakeUtilities
- {
- public static readonly BigInteger Zero = BigInteger.Zero;
- public static readonly BigInteger One = BigInteger.One;
- /// <summary>
- /// Return a value that can be used as x1 or x3 during round 1.
- /// The returned value is a random value in the range [0, q-1].
- /// </summary>
- public static BigInteger GenerateX1(BigInteger q, SecureRandom random)
- {
- BigInteger min = Zero;
- BigInteger max = q.Subtract(One);
- return BigIntegers.CreateRandomInRange(min, max, random);
- }
- /// <summary>
- /// Return a value that can be used as x2 or x4 during round 1.
- /// The returned value is a random value in the range [1, q-1].
- /// </summary>
- public static BigInteger GenerateX2(BigInteger q, SecureRandom random)
- {
- BigInteger min = One;
- BigInteger max = q.Subtract(One);
- return BigIntegers.CreateRandomInRange(min, max, random);
- }
- /// <summary>
- /// Converts the given password to a BigInteger
- /// for use in arithmetic calculations.
- /// </summary>
- public static BigInteger CalculateS(char[] password)
- {
- return new BigInteger(Encoding.UTF8.GetBytes(password));
- }
- /// <summary>
- /// Calculate g^x mod p as done in round 1.
- /// </summary>
- public static BigInteger CalculateGx(BigInteger p, BigInteger g, BigInteger x)
- {
- return g.ModPow(x, p);
- }
- /// <summary>
- /// Calculate ga as done in round 2.
- /// </summary>
- public static BigInteger CalculateGA(BigInteger p, BigInteger gx1, BigInteger gx3, BigInteger gx4)
- {
- // ga = g^(x1+x3+x4) = g^x1 * g^x3 * g^x4
- return gx1.Multiply(gx3).Multiply(gx4).Mod(p);
- }
- /// <summary>
- /// Calculate x2 * s as done in round 2.
- /// </summary>
- public static BigInteger CalculateX2s(BigInteger q, BigInteger x2, BigInteger s)
- {
- return x2.Multiply(s).Mod(q);
- }
- /// <summary>
- /// Calculate A as done in round 2.
- /// </summary>
- public static BigInteger CalculateA(BigInteger p, BigInteger q, BigInteger gA, BigInteger x2s)
- {
- // A = ga^(x*s)
- return gA.ModPow(x2s, p);
- }
- /// <summary>
- /// Calculate a zero knowledge proof of x using Schnorr's signature.
- /// The returned array has two elements {g^v, r = v-x*h} for x.
- /// </summary>
- public static BigInteger[] CalculateZeroKnowledgeProof(BigInteger p, BigInteger q, BigInteger g,
- BigInteger gx, BigInteger x, string participantId, IDigest digest, SecureRandom random)
- {
- /* Generate a random v, and compute g^v */
- BigInteger vMin = Zero;
- BigInteger vMax = q.Subtract(One);
- BigInteger v = BigIntegers.CreateRandomInRange(vMin, vMax, random);
- BigInteger gv = g.ModPow(v, p);
- BigInteger h = CalculateHashForZeroKnowledgeProof(g, gv, gx, participantId, digest); // h
- return new BigInteger[]
- {
- gv,
- v.Subtract(x.Multiply(h)).Mod(q) // r = v-x*h
- };
- }
- private static BigInteger CalculateHashForZeroKnowledgeProof(BigInteger g, BigInteger gr, BigInteger gx,
- string participantId, IDigest digest)
- {
- digest.Reset();
- UpdateDigestIncludingSize(digest, g);
- UpdateDigestIncludingSize(digest, gr);
- UpdateDigestIncludingSize(digest, gx);
- UpdateDigestIncludingSize(digest, participantId);
- byte[] output = DigestUtilities.DoFinal(digest);
- return new BigInteger(output);
- }
- /// <summary>
- /// Validates that g^x4 is not 1.
- /// throws CryptoException if g^x4 is 1
- /// </summary>
- public static void ValidateGx4(BigInteger gx4)
- {
- if (gx4.Equals(One))
- throw new CryptoException("g^x validation failed. g^x should not be 1.");
- }
- /// <summary>
- /// Validates that ga is not 1.
- ///
- /// As described by Feng Hao...
- /// Alice could simply check ga != 1 to ensure it is a generator.
- /// In fact, as we will explain in Section 3, (x1 + x3 + x4 ) is random over Zq even in the face of active attacks.
- /// Hence, the probability for ga = 1 is extremely small - on the order of 2^160 for 160-bit q.
- ///
- /// throws CryptoException if ga is 1
- /// </summary>
- public static void ValidateGa(BigInteger ga)
- {
- if (ga.Equals(One))
- throw new CryptoException("ga is equal to 1. It should not be. The chances of this happening are on the order of 2^160 for a 160-bit q. Try again.");
- }
- /// <summary>
- /// Validates the zero knowledge proof (generated by
- /// calculateZeroKnowledgeProof(BigInteger, BigInteger, BigInteger, BigInteger, BigInteger, string, Digest, SecureRandom)
- /// is correct.
- ///
- /// throws CryptoException if the zero knowledge proof is not correct
- /// </summary>
- public static void ValidateZeroKnowledgeProof(BigInteger p, BigInteger q, BigInteger g,
- BigInteger gx, BigInteger[] zeroKnowledgeProof, string participantId, IDigest digest)
- {
- /* sig={g^v,r} */
- BigInteger gv = zeroKnowledgeProof[0];
- BigInteger r = zeroKnowledgeProof[1];
- BigInteger h = CalculateHashForZeroKnowledgeProof(g, gv, gx, participantId, digest);
- if (!(gx.CompareTo(Zero) == 1 && // g^x > 0
- gx.CompareTo(p) == -1 && // g^x < p
- gx.ModPow(q, p).CompareTo(One) == 0 && // g^x^q mod q = 1
- /*
- * Below, I took a straightforward way to compute g^r * g^x^h,
- * which needs 2 exp. Using a simultaneous computation technique
- * would only need 1 exp.
- */
- g.ModPow(r, p).Multiply(gx.ModPow(h, p)).Mod(p).CompareTo(gv) == 0)) // g^v=g^r * g^x^h
- {
- throw new CryptoException("Zero-knowledge proof validation failed");
- }
- }
- /// <summary>
- /// Calculates the keying material, which can be done after round 2 has completed.
- /// A session key must be derived from this key material using a secure key derivation function (KDF).
- /// The KDF used to derive the key is handled externally (i.e. not by JPAKEParticipant).
- ///
- /// KeyingMaterial = (B/g^{x2*x4*s})^x2
- /// </summary>
- public static BigInteger CalculateKeyingMaterial(BigInteger p, BigInteger q,
- BigInteger gx4, BigInteger x2, BigInteger s, BigInteger B)
- {
- return gx4.ModPow(x2.Multiply(s).Negate().Mod(q), p).Multiply(B).ModPow(x2, p);
- }
- /// <summary>
- /// Validates that the given participant ids are not equal.
- /// (For the J-PAKE exchange, each participant must use a unique id.)
- ///
- /// Throws CryptoException if the participantId strings are equal.
- /// </summary>
- public static void ValidateParticipantIdsDiffer(string participantId1, string participantId2)
- {
- if (participantId1.Equals(participantId2))
- {
- throw new CryptoException(
- "Both participants are using the same participantId ("
- + participantId1
- + "). This is not allowed. "
- + "Each participant must use a unique participantId.");
- }
- }
- /// <summary>
- /// Validates that the given participant ids are equal.
- /// This is used to ensure that the payloads received from
- /// each round all come from the same participant.
- /// </summary>
- public static void ValidateParticipantIdsEqual(string expectedParticipantId, string actualParticipantId)
- {
- if (!expectedParticipantId.Equals(actualParticipantId))
- {
- throw new CryptoException(
- "Received payload from incorrect partner ("
- + actualParticipantId
- + "). Expected to receive payload from "
- + expectedParticipantId
- + ".");
- }
- }
- /// <summary>
- /// Validates that the given object is not null.
- /// throws NullReferenceException if the object is null.
- /// </summary>
- /// <param name="obj">object in question</param>
- /// <param name="description">name of the object (to be used in exception message)</param>
- public static void ValidateNotNull(object obj, string description)
- {
- if (obj == null)
- throw new ArgumentNullException(description);
- }
- /// <summary>
- /// Calculates the MacTag (to be used for key confirmation), as defined by
- /// <a href="http://csrc.nist.gov/publications/nistpubs/800-56A/SP800-56A_Revision1_Mar08-2007.pdf">NIST SP 800-56A Revision 1</a>,
- /// Section 8.2 Unilateral Key Confirmation for Key Agreement Schemes.
- ///
- /// MacTag = HMAC(MacKey, MacLen, MacData)
- /// MacKey = H(K || "JPAKE_KC")
- /// MacData = "KC_1_U" || participantId || partnerParticipantId || gx1 || gx2 || gx3 || gx4
- ///
- /// Note that both participants use "KC_1_U" because the sender of the round 3 message
- /// is always the initiator for key confirmation.
- ///
- /// HMAC = {@link HMac} used with the given {@link Digest}
- /// H = The given {@link Digest}
- /// MacLen = length of MacTag
- /// </summary>
- public static BigInteger CalculateMacTag(string participantId, string partnerParticipantId,
- BigInteger gx1, BigInteger gx2, BigInteger gx3, BigInteger gx4, BigInteger keyingMaterial, IDigest digest)
- {
- byte[] macKey = CalculateMacKey(keyingMaterial, digest);
- HMac mac = new HMac(digest);
- mac.Init(new KeyParameter(macKey));
- Arrays.Fill(macKey, (byte)0);
- /*
- * MacData = "KC_1_U" || participantId_Alice || participantId_Bob || gx1 || gx2 || gx3 || gx4.
- */
- UpdateMac(mac, "KC_1_U");
- UpdateMac(mac, participantId);
- UpdateMac(mac, partnerParticipantId);
- UpdateMac(mac, gx1);
- UpdateMac(mac, gx2);
- UpdateMac(mac, gx3);
- UpdateMac(mac, gx4);
- byte[] macOutput = MacUtilities.DoFinal(mac);
- return new BigInteger(macOutput);
- }
- /// <summary>
- /// Calculates the MacKey (i.e. the key to use when calculating the MagTag for key confirmation).
- ///
- /// MacKey = H(K || "JPAKE_KC")
- /// </summary>
- private static byte[] CalculateMacKey(BigInteger keyingMaterial, IDigest digest)
- {
- digest.Reset();
- UpdateDigest(digest, keyingMaterial);
- /*
- * This constant is used to ensure that the macKey is NOT the same as the derived key.
- */
- UpdateDigest(digest, "JPAKE_KC");
- return DigestUtilities.DoFinal(digest);
- }
- /// <summary>
- /// Validates the MacTag received from the partner participant.
- ///
- /// throws CryptoException if the participantId strings are equal.
- /// </summary>
- public static void ValidateMacTag(string participantId, string partnerParticipantId,
- BigInteger gx1, BigInteger gx2, BigInteger gx3, BigInteger gx4,
- BigInteger keyingMaterial, IDigest digest, BigInteger partnerMacTag)
- {
- /*
- * Calculate the expected MacTag using the parameters as the partner
- * would have used when the partner called calculateMacTag.
- *
- * i.e. basically all the parameters are reversed.
- * participantId <-> partnerParticipantId
- * x1 <-> x3
- * x2 <-> x4
- */
- BigInteger expectedMacTag = CalculateMacTag(partnerParticipantId, participantId, gx3, gx4, gx1, gx2, keyingMaterial, digest);
- if (!expectedMacTag.Equals(partnerMacTag))
- {
- throw new CryptoException(
- "Partner MacTag validation failed. "
- + "Therefore, the password, MAC, or digest algorithm of each participant does not match.");
- }
- }
- private static void UpdateDigest(IDigest digest, BigInteger bigInteger)
- {
- UpdateDigest(digest, BigIntegers.AsUnsignedByteArray(bigInteger));
- }
- private static void UpdateDigest(IDigest digest, string str)
- {
- UpdateDigest(digest, Encoding.UTF8.GetBytes(str));
- }
- private static void UpdateDigest(IDigest digest, byte[] bytes)
- {
- digest.BlockUpdate(bytes, 0, bytes.Length);
- Arrays.Fill(bytes, (byte)0);
- }
- private static void UpdateDigestIncludingSize(IDigest digest, BigInteger bigInteger)
- {
- UpdateDigestIncludingSize(digest, BigIntegers.AsUnsignedByteArray(bigInteger));
- }
- private static void UpdateDigestIncludingSize(IDigest digest, string str)
- {
- UpdateDigestIncludingSize(digest, Encoding.UTF8.GetBytes(str));
- }
- private static void UpdateDigestIncludingSize(IDigest digest, byte[] bytes)
- {
- digest.BlockUpdate(IntToByteArray(bytes.Length), 0, 4);
- digest.BlockUpdate(bytes, 0, bytes.Length);
- Arrays.Fill(bytes, (byte)0);
- }
- private static void UpdateMac(IMac mac, BigInteger bigInteger)
- {
- UpdateMac(mac, BigIntegers.AsUnsignedByteArray(bigInteger));
- }
- private static void UpdateMac(IMac mac, string str)
- {
- UpdateMac(mac, Encoding.UTF8.GetBytes(str));
- }
- private static void UpdateMac(IMac mac, byte[] bytes)
- {
- mac.BlockUpdate(bytes, 0, bytes.Length);
- Arrays.Fill(bytes, (byte)0);
- }
- private static byte[] IntToByteArray(int value)
- {
- return Pack.UInt32_To_BE((uint)value);
- }
- }
- }
- #pragma warning restore
- #endif
|