init commit
This commit is contained in:
@@ -0,0 +1,220 @@
|
||||
using System;
|
||||
using Org.BouncyCastle.Crypto.Digests;
|
||||
using Org.BouncyCastle.Math;
|
||||
using Org.BouncyCastle.Security;
|
||||
|
||||
namespace Org.BouncyCastle.Crypto.Agreement.JPake;
|
||||
|
||||
public class JPakeParticipant
|
||||
{
|
||||
public static readonly int STATE_INITIALIZED = 0;
|
||||
|
||||
public static readonly int STATE_ROUND_1_CREATED = 10;
|
||||
|
||||
public static readonly int STATE_ROUND_1_VALIDATED = 20;
|
||||
|
||||
public static readonly int STATE_ROUND_2_CREATED = 30;
|
||||
|
||||
public static readonly int STATE_ROUND_2_VALIDATED = 40;
|
||||
|
||||
public static readonly int STATE_KEY_CALCULATED = 50;
|
||||
|
||||
public static readonly int STATE_ROUND_3_CREATED = 60;
|
||||
|
||||
public static readonly int STATE_ROUND_3_VALIDATED = 70;
|
||||
|
||||
private string participantId;
|
||||
|
||||
private char[] password;
|
||||
|
||||
private IDigest digest;
|
||||
|
||||
private readonly SecureRandom random;
|
||||
|
||||
private readonly BigInteger p;
|
||||
|
||||
private readonly BigInteger q;
|
||||
|
||||
private readonly BigInteger g;
|
||||
|
||||
private string partnerParticipantId;
|
||||
|
||||
private BigInteger x1;
|
||||
|
||||
private BigInteger x2;
|
||||
|
||||
private BigInteger gx1;
|
||||
|
||||
private BigInteger gx2;
|
||||
|
||||
private BigInteger gx3;
|
||||
|
||||
private BigInteger gx4;
|
||||
|
||||
private BigInteger b;
|
||||
|
||||
private int state;
|
||||
|
||||
public virtual int State => state;
|
||||
|
||||
public JPakeParticipant(string participantId, char[] password)
|
||||
: this(participantId, password, JPakePrimeOrderGroups.NIST_3072)
|
||||
{
|
||||
}
|
||||
|
||||
public JPakeParticipant(string participantId, char[] password, JPakePrimeOrderGroup group)
|
||||
: this(participantId, password, group, new Sha256Digest(), new SecureRandom())
|
||||
{
|
||||
}
|
||||
|
||||
public JPakeParticipant(string participantId, char[] password, JPakePrimeOrderGroup group, IDigest digest, SecureRandom random)
|
||||
{
|
||||
JPakeUtilities.ValidateNotNull(participantId, "participantId");
|
||||
JPakeUtilities.ValidateNotNull(password, "password");
|
||||
JPakeUtilities.ValidateNotNull(group, "p");
|
||||
JPakeUtilities.ValidateNotNull(digest, "digest");
|
||||
JPakeUtilities.ValidateNotNull(random, "random");
|
||||
if (password.Length == 0)
|
||||
{
|
||||
throw new ArgumentException("Password must not be empty.");
|
||||
}
|
||||
this.participantId = participantId;
|
||||
this.password = new char[password.Length];
|
||||
Array.Copy(password, this.password, password.Length);
|
||||
p = group.P;
|
||||
q = group.Q;
|
||||
g = group.G;
|
||||
this.digest = digest;
|
||||
this.random = random;
|
||||
state = STATE_INITIALIZED;
|
||||
}
|
||||
|
||||
public virtual JPakeRound1Payload CreateRound1PayloadToSend()
|
||||
{
|
||||
if (state >= STATE_ROUND_1_CREATED)
|
||||
{
|
||||
throw new InvalidOperationException("Round 1 payload already created for " + participantId);
|
||||
}
|
||||
x1 = JPakeUtilities.GenerateX1(q, random);
|
||||
x2 = JPakeUtilities.GenerateX2(q, random);
|
||||
gx1 = JPakeUtilities.CalculateGx(p, g, x1);
|
||||
gx2 = JPakeUtilities.CalculateGx(p, g, x2);
|
||||
BigInteger[] knowledgeProofForX = JPakeUtilities.CalculateZeroKnowledgeProof(p, q, g, gx1, x1, participantId, digest, random);
|
||||
BigInteger[] knowledgeProofForX2 = JPakeUtilities.CalculateZeroKnowledgeProof(p, q, g, gx2, x2, participantId, digest, random);
|
||||
state = STATE_ROUND_1_CREATED;
|
||||
return new JPakeRound1Payload(participantId, gx1, gx2, knowledgeProofForX, knowledgeProofForX2);
|
||||
}
|
||||
|
||||
public virtual void ValidateRound1PayloadReceived(JPakeRound1Payload round1PayloadReceived)
|
||||
{
|
||||
if (state >= STATE_ROUND_1_VALIDATED)
|
||||
{
|
||||
throw new InvalidOperationException("Validation already attempted for round 1 payload for " + participantId);
|
||||
}
|
||||
partnerParticipantId = round1PayloadReceived.ParticipantId;
|
||||
gx3 = round1PayloadReceived.Gx1;
|
||||
gx4 = round1PayloadReceived.Gx2;
|
||||
BigInteger[] knowledgeProofForX = round1PayloadReceived.KnowledgeProofForX1;
|
||||
BigInteger[] knowledgeProofForX2 = round1PayloadReceived.KnowledgeProofForX2;
|
||||
JPakeUtilities.ValidateParticipantIdsDiffer(participantId, round1PayloadReceived.ParticipantId);
|
||||
JPakeUtilities.ValidateGx4(gx4);
|
||||
JPakeUtilities.ValidateZeroKnowledgeProof(p, q, g, gx3, knowledgeProofForX, round1PayloadReceived.ParticipantId, digest);
|
||||
JPakeUtilities.ValidateZeroKnowledgeProof(p, q, g, gx4, knowledgeProofForX2, round1PayloadReceived.ParticipantId, digest);
|
||||
state = STATE_ROUND_1_VALIDATED;
|
||||
}
|
||||
|
||||
public virtual JPakeRound2Payload CreateRound2PayloadToSend()
|
||||
{
|
||||
if (state >= STATE_ROUND_2_CREATED)
|
||||
{
|
||||
throw new InvalidOperationException("Round 2 payload already created for " + participantId);
|
||||
}
|
||||
if (state < STATE_ROUND_1_VALIDATED)
|
||||
{
|
||||
throw new InvalidOperationException("Round 1 payload must be validated prior to creating round 2 payload for " + participantId);
|
||||
}
|
||||
BigInteger gA = JPakeUtilities.CalculateGA(p, gx1, gx3, gx4);
|
||||
BigInteger s = JPakeUtilities.CalculateS(password);
|
||||
BigInteger bigInteger = JPakeUtilities.CalculateX2s(q, x2, s);
|
||||
BigInteger bigInteger2 = JPakeUtilities.CalculateA(p, q, gA, bigInteger);
|
||||
BigInteger[] knowledgeProofForX2s = JPakeUtilities.CalculateZeroKnowledgeProof(p, q, gA, bigInteger2, bigInteger, participantId, digest, random);
|
||||
state = STATE_ROUND_2_CREATED;
|
||||
return new JPakeRound2Payload(participantId, bigInteger2, knowledgeProofForX2s);
|
||||
}
|
||||
|
||||
public virtual void ValidateRound2PayloadReceived(JPakeRound2Payload round2PayloadReceived)
|
||||
{
|
||||
if (state >= STATE_ROUND_2_VALIDATED)
|
||||
{
|
||||
throw new InvalidOperationException("Validation already attempted for round 2 payload for " + participantId);
|
||||
}
|
||||
if (state < STATE_ROUND_1_VALIDATED)
|
||||
{
|
||||
throw new InvalidOperationException("Round 1 payload must be validated prior to validation round 2 payload for " + participantId);
|
||||
}
|
||||
BigInteger ga = JPakeUtilities.CalculateGA(p, gx3, gx1, gx2);
|
||||
b = round2PayloadReceived.A;
|
||||
BigInteger[] knowledgeProofForX2s = round2PayloadReceived.KnowledgeProofForX2s;
|
||||
JPakeUtilities.ValidateParticipantIdsDiffer(participantId, round2PayloadReceived.ParticipantId);
|
||||
JPakeUtilities.ValidateParticipantIdsEqual(partnerParticipantId, round2PayloadReceived.ParticipantId);
|
||||
JPakeUtilities.ValidateGa(ga);
|
||||
JPakeUtilities.ValidateZeroKnowledgeProof(p, q, ga, b, knowledgeProofForX2s, round2PayloadReceived.ParticipantId, digest);
|
||||
state = STATE_ROUND_2_VALIDATED;
|
||||
}
|
||||
|
||||
public virtual BigInteger CalculateKeyingMaterial()
|
||||
{
|
||||
if (state >= STATE_KEY_CALCULATED)
|
||||
{
|
||||
throw new InvalidOperationException("Key already calculated for " + participantId);
|
||||
}
|
||||
if (state < STATE_ROUND_2_VALIDATED)
|
||||
{
|
||||
throw new InvalidOperationException("Round 2 payload must be validated prior to creating key for " + participantId);
|
||||
}
|
||||
BigInteger s = JPakeUtilities.CalculateS(password);
|
||||
Array.Clear(password, 0, password.Length);
|
||||
password = null;
|
||||
BigInteger result = JPakeUtilities.CalculateKeyingMaterial(p, q, gx4, x2, s, b);
|
||||
x1 = null;
|
||||
x2 = null;
|
||||
b = null;
|
||||
state = STATE_KEY_CALCULATED;
|
||||
return result;
|
||||
}
|
||||
|
||||
public virtual JPakeRound3Payload CreateRound3PayloadToSend(BigInteger keyingMaterial)
|
||||
{
|
||||
if (state >= STATE_ROUND_3_CREATED)
|
||||
{
|
||||
throw new InvalidOperationException("Round 3 payload already created for " + participantId);
|
||||
}
|
||||
if (state < STATE_KEY_CALCULATED)
|
||||
{
|
||||
throw new InvalidOperationException("Keying material must be calculated prior to creating round 3 payload for " + participantId);
|
||||
}
|
||||
BigInteger magTag = JPakeUtilities.CalculateMacTag(participantId, partnerParticipantId, gx1, gx2, gx3, gx4, keyingMaterial, digest);
|
||||
state = STATE_ROUND_3_CREATED;
|
||||
return new JPakeRound3Payload(participantId, magTag);
|
||||
}
|
||||
|
||||
public virtual void ValidateRound3PayloadReceived(JPakeRound3Payload round3PayloadReceived, BigInteger keyingMaterial)
|
||||
{
|
||||
if (state >= STATE_ROUND_3_VALIDATED)
|
||||
{
|
||||
throw new InvalidOperationException("Validation already attempted for round 3 payload for " + participantId);
|
||||
}
|
||||
if (state < STATE_KEY_CALCULATED)
|
||||
{
|
||||
throw new InvalidOperationException("Keying material must be calculated prior to validating round 3 payload for " + participantId);
|
||||
}
|
||||
JPakeUtilities.ValidateParticipantIdsDiffer(participantId, round3PayloadReceived.ParticipantId);
|
||||
JPakeUtilities.ValidateParticipantIdsEqual(partnerParticipantId, round3PayloadReceived.ParticipantId);
|
||||
JPakeUtilities.ValidateMacTag(participantId, partnerParticipantId, gx1, gx2, gx3, gx4, keyingMaterial, digest, round3PayloadReceived.MacTag);
|
||||
gx1 = null;
|
||||
gx2 = null;
|
||||
gx3 = null;
|
||||
gx4 = null;
|
||||
state = STATE_ROUND_3_VALIDATED;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
using System;
|
||||
using Org.BouncyCastle.Math;
|
||||
|
||||
namespace Org.BouncyCastle.Crypto.Agreement.JPake;
|
||||
|
||||
public class JPakePrimeOrderGroup
|
||||
{
|
||||
private readonly BigInteger p;
|
||||
|
||||
private readonly BigInteger q;
|
||||
|
||||
private readonly BigInteger g;
|
||||
|
||||
public virtual BigInteger P => p;
|
||||
|
||||
public virtual BigInteger Q => q;
|
||||
|
||||
public virtual BigInteger G => g;
|
||||
|
||||
public JPakePrimeOrderGroup(BigInteger p, BigInteger q, BigInteger g)
|
||||
: this(p, q, g, skipChecks: false)
|
||||
{
|
||||
}
|
||||
|
||||
public JPakePrimeOrderGroup(BigInteger p, BigInteger q, BigInteger g, bool skipChecks)
|
||||
{
|
||||
JPakeUtilities.ValidateNotNull(p, "p");
|
||||
JPakeUtilities.ValidateNotNull(q, "q");
|
||||
JPakeUtilities.ValidateNotNull(g, "g");
|
||||
if (!skipChecks)
|
||||
{
|
||||
if (!p.Subtract(JPakeUtilities.One).Mod(q).Equals(JPakeUtilities.Zero))
|
||||
{
|
||||
throw new ArgumentException("p-1 must be evenly divisible by q");
|
||||
}
|
||||
if (g.CompareTo(BigInteger.Two) == -1 || g.CompareTo(p.Subtract(JPakeUtilities.One)) == 1)
|
||||
{
|
||||
throw new ArgumentException("g must be in [2, p-1]");
|
||||
}
|
||||
if (!g.ModPow(q, p).Equals(JPakeUtilities.One))
|
||||
{
|
||||
throw new ArgumentException("g^q mod p must equal 1");
|
||||
}
|
||||
if (!p.IsProbablePrime(20))
|
||||
{
|
||||
throw new ArgumentException("p must be prime");
|
||||
}
|
||||
if (!q.IsProbablePrime(20))
|
||||
{
|
||||
throw new ArgumentException("q must be prime");
|
||||
}
|
||||
}
|
||||
this.p = p;
|
||||
this.q = q;
|
||||
this.g = g;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
using Org.BouncyCastle.Math;
|
||||
|
||||
namespace Org.BouncyCastle.Crypto.Agreement.JPake;
|
||||
|
||||
public class JPakePrimeOrderGroups
|
||||
{
|
||||
public static readonly JPakePrimeOrderGroup SUN_JCE_1024 = new JPakePrimeOrderGroup(new BigInteger("fd7f53811d75122952df4a9c2eece4e7f611b7523cef4400c31e3f80b6512669455d402251fb593d8d58fabfc5f5ba30f6cb9b556cd7813b801d346ff26660b76b9950a5a49f9fe8047b1022c24fbba9d7feb7c61bf83b57e7c6a8a6150f04fb83f6d3c51ec3023554135a169132f675f3ae2b61d72aeff22203199dd14801c7", 16), new BigInteger("9760508f15230bccb292b982a2eb840bf0581cf5", 16), new BigInteger("f7e1a085d69b3ddecbbcab5c36b857b97994afbbfa3aea82f9574c0b3d0782675159578ebad4594fe67107108180b449167123e84c281613b7cf09328cc8a6e13c167a8b547c8d28e0a3ae1e2bb3a675916ea37f0bfa213562f1fb627a01243bcca4f1bea8519089a883dfe15ae59f06928b665e807b552564014c3bfecf492a", 16), skipChecks: true);
|
||||
|
||||
public static readonly JPakePrimeOrderGroup NIST_2048 = new JPakePrimeOrderGroup(new BigInteger("C196BA05AC29E1F9C3C72D56DFFC6154A033F1477AC88EC37F09BE6C5BB95F51C296DD20D1A28A067CCC4D4316A4BD1DCA55ED1066D438C35AEBAABF57E7DAE428782A95ECA1C143DB701FD48533A3C18F0FE23557EA7AE619ECACC7E0B51652A8776D02A425567DED36EABD90CA33A1E8D988F0BBB92D02D1D20290113BB562CE1FC856EEB7CDD92D33EEA6F410859B179E7E789A8F75F645FAE2E136D252BFFAFF89528945C1ABE705A38DBC2D364AADE99BE0D0AAD82E5320121496DC65B3930E38047294FF877831A16D5228418DE8AB275D7D75651CEFED65F78AFC3EA7FE4D79B35F62A0402A1117599ADAC7B269A59F353CF450E6982D3B1702D9CA83", 16), new BigInteger("90EAF4D1AF0708B1B612FF35E0A2997EB9E9D263C9CE659528945C0D", 16), new BigInteger("A59A749A11242C58C894E9E5A91804E8FA0AC64B56288F8D47D51B1EDC4D65444FECA0111D78F35FC9FDD4CB1F1B79A3BA9CBEE83A3F811012503C8117F98E5048B089E387AF6949BF8784EBD9EF45876F2E6A5A495BE64B6E770409494B7FEE1DBB1E4B2BC2A53D4F893D418B7159592E4FFFDF6969E91D770DAEBD0B5CB14C00AD68EC7DC1E5745EA55C706C4A1C5C88964E34D09DEB753AD418C1AD0F4FDFD049A955E5D78491C0B7A2F1575A008CCD727AB376DB6E695515B05BD412F5B8C2F4C77EE10DA48ABD53F5DD498927EE7B692BBBCDA2FB23A516C5B4533D73980B2A3B60E384ED200AE21B40D273651AD6060C13D97FD69AA13C5611A51B9085", 16), skipChecks: true);
|
||||
|
||||
public static readonly JPakePrimeOrderGroup NIST_3072 = new JPakePrimeOrderGroup(new BigInteger("90066455B5CFC38F9CAA4A48B4281F292C260FEEF01FD61037E56258A7795A1C7AD46076982CE6BB956936C6AB4DCFE05E6784586940CA544B9B2140E1EB523F009D20A7E7880E4E5BFA690F1B9004A27811CD9904AF70420EEFD6EA11EF7DA129F58835FF56B89FAA637BC9AC2EFAAB903402229F491D8D3485261CD068699B6BA58A1DDBBEF6DB51E8FE34E8A78E542D7BA351C21EA8D8F1D29F5D5D15939487E27F4416B0CA632C59EFD1B1EB66511A5A0FBF615B766C5862D0BD8A3FE7A0E0DA0FB2FE1FCB19E8F9996A8EA0FCCDE538175238FC8B0EE6F29AF7F642773EBE8CD5402415A01451A840476B2FCEB0E388D30D4B376C37FE401C2A2C2F941DAD179C540C1C8CE030D460C4D983BE9AB0B20F69144C1AE13F9383EA1C08504FB0BF321503EFE43488310DD8DC77EC5B8349B8BFE97C2C560EA878DE87C11E3D597F1FEA742D73EEC7F37BE43949EF1A0D15C3F3E3FC0A8335617055AC91328EC22B50FC15B941D3D1624CD88BC25F3E941FDDC6200689581BFEC416B4B2CB73", 16), new BigInteger("CFA0478A54717B08CE64805B76E5B14249A77A4838469DF7F7DC987EFCCFB11D", 16), new BigInteger("5E5CBA992E0A680D885EB903AEA78E4A45A469103D448EDE3B7ACCC54D521E37F84A4BDD5B06B0970CC2D2BBB715F7B82846F9A0C393914C792E6A923E2117AB805276A975AADB5261D91673EA9AAFFEECBFA6183DFCB5D3B7332AA19275AFA1F8EC0B60FB6F66CC23AE4870791D5982AAD1AA9485FD8F4A60126FEB2CF05DB8A7F0F09B3397F3937F2E90B9E5B9C9B6EFEF642BC48351C46FB171B9BFA9EF17A961CE96C7E7A7CC3D3D03DFAD1078BA21DA425198F07D2481622BCE45969D9C4D6063D72AB7A0F08B2F49A7CC6AF335E08C4720E31476B67299E231F8BD90B39AC3AE3BE0C6B6CACEF8289A2E2873D58E51E029CAFBD55E6841489AB66B5B4B9BA6E2F784660896AFF387D92844CCB8B69475496DE19DA2E58259B090489AC8E62363CDF82CFD8EF2A427ABCD65750B506F56DDE3B988567A88126B914D7828E2B63A6D7ED0747EC59E0E0A23CE7D8A74C1D2C2A7AFB6A29799620F00E11C33787F7DED3B30E1A22D09F1FBDA1ABBBFBF25CAE05A13F812E34563F99410E73B", 16), skipChecks: true);
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
using System;
|
||||
using Org.BouncyCastle.Math;
|
||||
|
||||
namespace Org.BouncyCastle.Crypto.Agreement.JPake;
|
||||
|
||||
public class JPakeRound1Payload
|
||||
{
|
||||
private readonly string participantId;
|
||||
|
||||
private readonly BigInteger gx1;
|
||||
|
||||
private readonly BigInteger gx2;
|
||||
|
||||
private readonly BigInteger[] knowledgeProofForX1;
|
||||
|
||||
private readonly BigInteger[] knowledgeProofForX2;
|
||||
|
||||
public virtual string ParticipantId => participantId;
|
||||
|
||||
public virtual BigInteger Gx1 => gx1;
|
||||
|
||||
public virtual BigInteger Gx2 => gx2;
|
||||
|
||||
public virtual BigInteger[] KnowledgeProofForX1
|
||||
{
|
||||
get
|
||||
{
|
||||
BigInteger[] array = new BigInteger[knowledgeProofForX1.Length];
|
||||
Array.Copy(knowledgeProofForX1, array, knowledgeProofForX1.Length);
|
||||
return array;
|
||||
}
|
||||
}
|
||||
|
||||
public virtual BigInteger[] KnowledgeProofForX2
|
||||
{
|
||||
get
|
||||
{
|
||||
BigInteger[] array = new BigInteger[knowledgeProofForX2.Length];
|
||||
Array.Copy(knowledgeProofForX2, array, knowledgeProofForX2.Length);
|
||||
return array;
|
||||
}
|
||||
}
|
||||
|
||||
public JPakeRound1Payload(string participantId, BigInteger gx1, BigInteger gx2, BigInteger[] knowledgeProofForX1, BigInteger[] knowledgeProofForX2)
|
||||
{
|
||||
JPakeUtilities.ValidateNotNull(participantId, "participantId");
|
||||
JPakeUtilities.ValidateNotNull(gx1, "gx1");
|
||||
JPakeUtilities.ValidateNotNull(gx2, "gx2");
|
||||
JPakeUtilities.ValidateNotNull(knowledgeProofForX1, "knowledgeProofForX1");
|
||||
JPakeUtilities.ValidateNotNull(knowledgeProofForX2, "knowledgeProofForX2");
|
||||
this.participantId = participantId;
|
||||
this.gx1 = gx1;
|
||||
this.gx2 = gx2;
|
||||
this.knowledgeProofForX1 = new BigInteger[knowledgeProofForX1.Length];
|
||||
Array.Copy(knowledgeProofForX1, this.knowledgeProofForX1, knowledgeProofForX1.Length);
|
||||
this.knowledgeProofForX2 = new BigInteger[knowledgeProofForX2.Length];
|
||||
Array.Copy(knowledgeProofForX2, this.knowledgeProofForX2, knowledgeProofForX2.Length);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
using System;
|
||||
using Org.BouncyCastle.Math;
|
||||
|
||||
namespace Org.BouncyCastle.Crypto.Agreement.JPake;
|
||||
|
||||
public class JPakeRound2Payload
|
||||
{
|
||||
private readonly string participantId;
|
||||
|
||||
private readonly BigInteger a;
|
||||
|
||||
private readonly BigInteger[] knowledgeProofForX2s;
|
||||
|
||||
public virtual string ParticipantId => participantId;
|
||||
|
||||
public virtual BigInteger A => a;
|
||||
|
||||
public virtual BigInteger[] KnowledgeProofForX2s
|
||||
{
|
||||
get
|
||||
{
|
||||
BigInteger[] array = new BigInteger[knowledgeProofForX2s.Length];
|
||||
Array.Copy(knowledgeProofForX2s, array, knowledgeProofForX2s.Length);
|
||||
return array;
|
||||
}
|
||||
}
|
||||
|
||||
public JPakeRound2Payload(string participantId, BigInteger a, BigInteger[] knowledgeProofForX2s)
|
||||
{
|
||||
JPakeUtilities.ValidateNotNull(participantId, "participantId");
|
||||
JPakeUtilities.ValidateNotNull(a, "a");
|
||||
JPakeUtilities.ValidateNotNull(knowledgeProofForX2s, "knowledgeProofForX2s");
|
||||
this.participantId = participantId;
|
||||
this.a = a;
|
||||
this.knowledgeProofForX2s = new BigInteger[knowledgeProofForX2s.Length];
|
||||
knowledgeProofForX2s.CopyTo(this.knowledgeProofForX2s, 0);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
using Org.BouncyCastle.Math;
|
||||
|
||||
namespace Org.BouncyCastle.Crypto.Agreement.JPake;
|
||||
|
||||
public class JPakeRound3Payload
|
||||
{
|
||||
private readonly string participantId;
|
||||
|
||||
private readonly BigInteger macTag;
|
||||
|
||||
public virtual string ParticipantId => participantId;
|
||||
|
||||
public virtual BigInteger MacTag => macTag;
|
||||
|
||||
public JPakeRound3Payload(string participantId, BigInteger magTag)
|
||||
{
|
||||
this.participantId = participantId;
|
||||
macTag = magTag;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,226 @@
|
||||
using System;
|
||||
using System.Text;
|
||||
using Org.BouncyCastle.Crypto.Macs;
|
||||
using Org.BouncyCastle.Crypto.Parameters;
|
||||
using Org.BouncyCastle.Crypto.Utilities;
|
||||
using Org.BouncyCastle.Math;
|
||||
using Org.BouncyCastle.Security;
|
||||
using Org.BouncyCastle.Utilities;
|
||||
|
||||
namespace Org.BouncyCastle.Crypto.Agreement.JPake;
|
||||
|
||||
public abstract class JPakeUtilities
|
||||
{
|
||||
public static readonly BigInteger Zero = BigInteger.Zero;
|
||||
|
||||
public static readonly BigInteger One = BigInteger.One;
|
||||
|
||||
public static BigInteger GenerateX1(BigInteger q, SecureRandom random)
|
||||
{
|
||||
BigInteger zero = Zero;
|
||||
BigInteger max = q.Subtract(One);
|
||||
return BigIntegers.CreateRandomInRange(zero, max, random);
|
||||
}
|
||||
|
||||
public static BigInteger GenerateX2(BigInteger q, SecureRandom random)
|
||||
{
|
||||
BigInteger one = One;
|
||||
BigInteger max = q.Subtract(One);
|
||||
return BigIntegers.CreateRandomInRange(one, max, random);
|
||||
}
|
||||
|
||||
public static BigInteger CalculateS(char[] password)
|
||||
{
|
||||
return new BigInteger(Encoding.UTF8.GetBytes(password));
|
||||
}
|
||||
|
||||
public static BigInteger CalculateGx(BigInteger p, BigInteger g, BigInteger x)
|
||||
{
|
||||
return g.ModPow(x, p);
|
||||
}
|
||||
|
||||
public static BigInteger CalculateGA(BigInteger p, BigInteger gx1, BigInteger gx3, BigInteger gx4)
|
||||
{
|
||||
return gx1.Multiply(gx3).Multiply(gx4).Mod(p);
|
||||
}
|
||||
|
||||
public static BigInteger CalculateX2s(BigInteger q, BigInteger x2, BigInteger s)
|
||||
{
|
||||
return x2.Multiply(s).Mod(q);
|
||||
}
|
||||
|
||||
public static BigInteger CalculateA(BigInteger p, BigInteger q, BigInteger gA, BigInteger x2s)
|
||||
{
|
||||
return gA.ModPow(x2s, p);
|
||||
}
|
||||
|
||||
public static BigInteger[] CalculateZeroKnowledgeProof(BigInteger p, BigInteger q, BigInteger g, BigInteger gx, BigInteger x, string participantId, IDigest digest, SecureRandom random)
|
||||
{
|
||||
BigInteger zero = Zero;
|
||||
BigInteger max = q.Subtract(One);
|
||||
BigInteger bigInteger = BigIntegers.CreateRandomInRange(zero, max, random);
|
||||
BigInteger bigInteger2 = g.ModPow(bigInteger, p);
|
||||
BigInteger val = CalculateHashForZeroKnowledgeProof(g, bigInteger2, gx, participantId, digest);
|
||||
return new BigInteger[2]
|
||||
{
|
||||
bigInteger2,
|
||||
bigInteger.Subtract(x.Multiply(val)).Mod(q)
|
||||
};
|
||||
}
|
||||
|
||||
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[] bytes = DigestUtilities.DoFinal(digest);
|
||||
return new BigInteger(bytes);
|
||||
}
|
||||
|
||||
public static void ValidateGx4(BigInteger gx4)
|
||||
{
|
||||
if (gx4.Equals(One))
|
||||
{
|
||||
throw new CryptoException("g^x validation failed. g^x should not be 1.");
|
||||
}
|
||||
}
|
||||
|
||||
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.");
|
||||
}
|
||||
}
|
||||
|
||||
public static void ValidateZeroKnowledgeProof(BigInteger p, BigInteger q, BigInteger g, BigInteger gx, BigInteger[] zeroKnowledgeProof, string participantId, IDigest digest)
|
||||
{
|
||||
BigInteger bigInteger = zeroKnowledgeProof[0];
|
||||
BigInteger e = zeroKnowledgeProof[1];
|
||||
BigInteger e2 = CalculateHashForZeroKnowledgeProof(g, bigInteger, gx, participantId, digest);
|
||||
if (gx.CompareTo(Zero) != 1 || gx.CompareTo(p) != -1 || gx.ModPow(q, p).CompareTo(One) != 0 || g.ModPow(e, p).Multiply(gx.ModPow(e2, p)).Mod(p)
|
||||
.CompareTo(bigInteger) != 0)
|
||||
{
|
||||
throw new CryptoException("Zero-knowledge proof validation failed");
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
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.");
|
||||
}
|
||||
}
|
||||
|
||||
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 + ".");
|
||||
}
|
||||
}
|
||||
|
||||
public static void ValidateNotNull(object obj, string description)
|
||||
{
|
||||
if (obj == null)
|
||||
{
|
||||
throw new ArgumentNullException(description);
|
||||
}
|
||||
}
|
||||
|
||||
public static BigInteger CalculateMacTag(string participantId, string partnerParticipantId, BigInteger gx1, BigInteger gx2, BigInteger gx3, BigInteger gx4, BigInteger keyingMaterial, IDigest digest)
|
||||
{
|
||||
byte[] array = CalculateMacKey(keyingMaterial, digest);
|
||||
HMac hMac = new HMac(digest);
|
||||
hMac.Init(new KeyParameter(array));
|
||||
Arrays.Fill(array, 0);
|
||||
UpdateMac(hMac, "KC_1_U");
|
||||
UpdateMac(hMac, participantId);
|
||||
UpdateMac(hMac, partnerParticipantId);
|
||||
UpdateMac(hMac, gx1);
|
||||
UpdateMac(hMac, gx2);
|
||||
UpdateMac(hMac, gx3);
|
||||
UpdateMac(hMac, gx4);
|
||||
byte[] bytes = MacUtilities.DoFinal(hMac);
|
||||
return new BigInteger(bytes);
|
||||
}
|
||||
|
||||
private static byte[] CalculateMacKey(BigInteger keyingMaterial, IDigest digest)
|
||||
{
|
||||
digest.Reset();
|
||||
UpdateDigest(digest, keyingMaterial);
|
||||
UpdateDigest(digest, "JPAKE_KC");
|
||||
return DigestUtilities.DoFinal(digest);
|
||||
}
|
||||
|
||||
public static void ValidateMacTag(string participantId, string partnerParticipantId, BigInteger gx1, BigInteger gx2, BigInteger gx3, BigInteger gx4, BigInteger keyingMaterial, IDigest digest, BigInteger partnerMacTag)
|
||||
{
|
||||
BigInteger bigInteger = CalculateMacTag(partnerParticipantId, participantId, gx3, gx4, gx1, gx2, keyingMaterial, digest);
|
||||
if (!bigInteger.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, 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, 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, 0);
|
||||
}
|
||||
|
||||
private static byte[] IntToByteArray(int value)
|
||||
{
|
||||
return Pack.UInt32_To_BE((uint)value);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user