/// <summary> /// Creates a round 2 (zero-knowledge proof) DTO to send to the partner participant. /// </summary> /// <exception cref="InvalidOperationException"> /// Prior round (1) has not been completed yet, or method may have been called more than once. /// </exception> public ECJpakeRound2 CreateRound2ToSend() { if (ProtocolState >= State.Round2Created) { throw new InvalidOperationException("Round 2 payload already created."); } if (ProtocolState < State.Round1Validated) { throw new InvalidOperationException("Round 1 payload must be validated prior to creating Round 2 payload."); } var s1 = new BigInteger(_passwordBytes); ECPoint GA = _gx1.Add(_gx3).Add(_gx4); BigInteger x2s1 = _x2.Multiply(s1).Mod(_domain.N); ECPoint A = BasePointMultiplier.Multiply(GA, x2s1); ECPoint X2sV; BigInteger X2sR; CreateZeroKnowledgeProof(GA, x2s1, A, ParticipantId, out X2sV, out X2sR); var dto = new ECJpakeRound2 { ParticipantId = ParticipantId, A = A.GetEncoded(), X2sV = X2sV.GetEncoded(), X2sR = X2sR.ToByteArray() }; ProtocolState = State.Round2Created; return(dto); }
/// <summary> /// Validates the round 2 (zero-knowledge proof) DTO received from the partner participant. /// </summary> /// <param name="round2Received">Round 2 DTO received form partner participant.</param> /// <exception cref="InvalidOperationException"> /// Prior round (1) has not been completed yet, or method may have been called more than once. /// </exception> /// <exception cref="CryptoException"> /// Verification of zero-knowledge proof failed. Possible attempted impersonation, e.g. MiTM. /// </exception> public void ValidateRound2Received(ECJpakeRound2 round2Received) { Contract.Requires <InvalidOperationException>(ProtocolState < State.Round2Validated, "Validation already attempted for round 2 payload."); Contract.Requires <InvalidOperationException>(ProtocolState > State.Round1Validated, "Round 1 payload must be validated prior to validating round 2 payload."); Contract.Requires <ConfigurationInvalidException>(String.IsNullOrEmpty(round2Received.ParticipantId) == false, "Partner participant ID in round 2 DTO received is null or empty."); Contract.Requires <CryptoException>(PartnerParticipantId.Equals(round2Received.ParticipantId, StringComparison.Ordinal), "Partner participant ID of round 2 DTO does not match value from round 1."); ECPoint X4sV = _domain.Curve.DecodePoint(round2Received.X2sV); var X4sR = new BigInteger(round2Received.X2sR); _b = _domain.Curve.DecodePoint(round2Received.A); // Calculate GB : GX1 + GX3 + GX4 symmetrically ECPoint GB = _gx3.Add(_gx1).Add(_gx2); if (ZeroKnowledgeProofValid(GB, _b, X4sV, X4sR, PartnerParticipantId) == false) { throw new CryptoException("Round 2 validation failed. Possible impersonation attempt."); } ProtocolState = State.Round2Validated; }
/// <summary> /// Restores the state of an incomplete J-PAKE session, /// given private keys and DTO objects created/received from that session. /// </summary> /// <param name="x2">Private key.</param> /// <param name="round1Created">Round 1 created/sent.</param> /// <param name="round1Received">Round 1 received.</param> /// <param name="round2Created">Round 2 created/sent.</param> /// <param name="round2Received">Round 2 received.</param> /// <param name="round3Created">Round 3 created/sent.</param> public void RestoreState(byte[] x2, ECJpakeRound1 round1Created, ECJpakeRound1 round1Received = null, ECJpakeRound2 round2Created = null, ECJpakeRound2 round2Received = null, JpakeRound3 round3Created = null) { Contract.Requires(ProtocolState == State.Initialised, "Cannot restore state of already-active protocol session!"); Contract.Requires(round1Created != null); _gx1 = _domain.Curve.DecodePoint(round1Created.GX1); _gx2 = _domain.Curve.DecodePoint(round1Created.GX2); ProtocolState = State.Round1Created; if (round1Received != null) { if (String.IsNullOrEmpty(round1Received.ParticipantId)) { throw new ArgumentException("Partner participant ID in round 1 received is null or empty."); } PartnerParticipantId = round1Received.ParticipantId; _gx3 = _domain.Curve.DecodePoint(round1Received.GX1); _gx4 = _domain.Curve.DecodePoint(round1Received.GX2); ProtocolState = State.Round1Validated; } else { return; } if (round2Created != null) { ProtocolState = State.Round2Created; } else { return; } if (round2Received != null) { if (PartnerParticipantId.Equals(round2Received.ParticipantId, StringComparison.Ordinal) == false) { throw new ArgumentException("Partner participant ID of round 2 does not match value from round 1."); } _b = _domain.Curve.DecodePoint(round2Received.A); ProtocolState = State.Round2Validated; } else { return; } if (round3Created != null) { // Keying material has been calculated _b = _domain.Curve.DecodePoint(round2Received.A); ProtocolState = State.Round3Created; } else { if (x2.IsNullOrZeroLength()) { throw new ArgumentException("Session cannot be resumed without private key x2Export. Aborting."); } } }